<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/Masterminds/glide/cfg"
	gpath "github.com/Masterminds/glide/path"
	"github.com/bmatcuk/doublestar"
	"github.com/spf13/cobra"
)

var cmd = &cobra.Command{
	Use:   "glide-vc",
	Short: "glide vendor cleaner",
	Run:   glidevc,
}

type options struct {
	dryrun       bool
	onlyCode     bool
	noTests      bool
	noLegalFiles bool
	keepPatterns []string
}

var (
	opts         options
	codeSuffixes = []string{".go", ".c", ".s", ".S", ".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"}
)

const (
	goTestSuffix = "_test.go"
)

func init() {
	cmd.PersistentFlags().BoolVar(&opts.dryrun, "dryrun", false, "just output what will be removed")
	cmd.PersistentFlags().BoolVar(&opts.onlyCode, "only-code", false, "keep only go files (including go test files)")
	cmd.PersistentFlags().BoolVar(&opts.noTests, "no-tests", false, "remove also go test files (requires --only-code)")
	cmd.PersistentFlags().BoolVar(&opts.noLegalFiles, "no-legal-files", false, "remove also licenses and legal files")
	cmd.PersistentFlags().StringSliceVar(&opts.keepPatterns, "keep", []string{}, "A pattern to keep additional files inside needed packages. The pattern match will be relative to the deeper vendor dir. Supports double star (**) patterns. (see https://golang.org/pkg/path/filepath/#Match and https://github.com/bmatcuk/doublestar). Can be specified multiple times. For example to keep all the files with json extension use the '**/*.json' pattern.")
}

func main() {
	cmd.Execute()
}

func glidevc(cmd *cobra.Command, args []string) {
	if opts.noTests && !opts.onlyCode {
		fmt.Printf("--no-tests requires --only-code")
		os.Exit(1)
	}

	if err := cleanup(".", opts); err != nil {
		fmt.Print(err)
		os.Exit(1)
	}
	return
}

func cleanup(path string, opts options) error {
	lock, err := LoadGlideLockfile(path)
	if err != nil {
		return fmt.Errorf("Could not load lockfile: %v", err)
	}

	// The package list already have the path converted to the os specific
	// path separator, needed for future comparisons.
	pkgList := []string{}
	repoList := []string{}
	// TODO(sgotti) Should we also consider devImports?
	for _, imp := range lock.Imports {
		// This converts pkg separator "/" to os specific separator
		repoList = append(repoList, filepath.FromSlash(imp.Name))

		if len(imp.Subpackages) > 0 {
			for _, sp := range imp.Subpackages {
				// This converts pkg separator "/" to os specific separator
				pkgList = append(pkgList, filepath.Join(imp.Name, sp))
			}
		}
		// TODO(sgotti) we cannot skip the base import if it has subpackages
		// because glide doesn't write "." as a subpackage, otherwise if some
		// files in the base import are needed they will be removed.

		// This converts pkg separator "/" to os specific separator
		pkgList = append(pkgList, filepath.FromSlash(imp.Name))
	}

	vpath, err := gpath.Vendor()
	if err != nil {
		return err
	}
	if vpath == "" {
		return fmt.Errorf("cannot fine vendor dir")
	}

	type pathData struct {
		path  string
		isDir bool
	}
	var searchPath string
	markForKeep := map[string]pathData{}
	markForDelete := []pathData{}

	// Walk vendor directory
	searchPath = vpath + string(os.PathSeparator)
	err = filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if path == searchPath || path == vpath {
			return nil
		}

		localPath := strings.TrimPrefix(path, searchPath)

		lastVendorPath, err := getLastVendorPath(localPath)
		if err != nil {
			return err
		}
		if lastVendorPath == "" {
			lastVendorPath = localPath
		}

		keep := false
		for _, name := range pkgList {
			// If the file's parent directory is a needed package, keep it.
			if !info.IsDir() && filepath.Dir(lastVendorPath) == name {
				if opts.onlyCode {
					if opts.noTests && strings.HasSuffix(path, "_test.go") {
						keep = false
						continue
					}
					for _, suffix := range codeSuffixes {
						if strings.HasSuffix(path, suffix) {
							keep = true
						}
					}
					// Match keep patterns
					for _, keepPattern := range opts.keepPatterns {
						ok, err := doublestar.Match(keepPattern, lastVendorPath)
						// TODO(sgotti) if a bad pattern is encountered stop here. Actually there's no function to verify a pattern before using it, perhaps just a fake match at the start will work.
						if err != nil {
							return fmt.Errorf("bad pattern: %q", keepPattern)
						}
						if ok {
							keep = true
						}
					}
				} else {
					keep = true
				}
			}
		}

		// Keep all the legal files inside top repo dir and required package dirs
		for _, name := range append(repoList, pkgList...) {
			if !info.IsDir() && filepath.Dir(lastVendorPath) == name {
				if !opts.noLegalFiles {
					if IsLegalFile(path) {
						keep = true
					}
				}
			}
		}

		// If a directory is a needed package then keep it
		if keep == false && info.IsDir() {
			for _, name := range pkgList {
				if name == lastVendorPath {
					keep = true
				}
			}
		}

		if keep {
			// Keep also all parents of current path
			curpath := localPath
			for {
				curpath = filepath.Dir(curpath)
				if curpath == "." {
					break
				}
				if _, ok := markForKeep[curpath]; ok {
					// Already marked for keep
					break
				}
				markForKeep[curpath] = pathData{curpath, true}
			}

			// Mark for keep
			markForKeep[localPath] = pathData{localPath, info.IsDir()}
		}

		return nil
	})
	if err != nil {
		return err
	}

	// Generate deletion list
	err = filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			// Ignore not existant files due to previous removal of the parent directory
			if !os.IsNotExist(err) {
				return err
			}
		}
		localPath := strings.TrimPrefix(path, searchPath)
		if localPath == "" {
			return nil
		}
		if _, ok := markForKeep[localPath]; !ok {
			markForDelete = append(markForDelete, pathData{path, info.IsDir()})
			if info.IsDir() {
				// skip directory contents since it has been marked for removal
				return filepath.SkipDir
			}
		}
		return nil
	})
	if err != nil {
		return err
	}

	// Perform the actual delete.
	for _, marked := range markForDelete {
		localPath := strings.TrimPrefix(marked.path, searchPath)
		if marked.isDir {
			fmt.Printf("Removing unused dir: %s\n", localPath)
		} else {
			fmt.Printf("Removing unused file: %s\n", localPath)
		}
		if !opts.dryrun {
			rerr := os.RemoveAll(marked.path)
			if rerr != nil {
				return rerr
			}
		}
	}

	return nil
}

func getLastVendorPath(path string) (string, error) {
	curpath := path
	for {
		if curpath == "." {
			return "", nil
		}
		if filepath.Base(curpath) == "vendor" {
			return filepath.Rel(curpath, path)
		}
		curpath = filepath.Dir(curpath)
	}
}

// LoadLockfile loads the contents of a glide.lock file.
func LoadGlideLockfile(base string) (*cfg.Lockfile, error) {
	yml, err := ioutil.ReadFile(filepath.Join(base, gpath.LockFile))
	if err != nil {
		return nil, err
	}
	lock, err := cfg.LockfileFromYaml(yml)
	if err != nil {
		return nil, err
	}

	return lock, nil
}

// File lists and code took from https://github.com/client9/gosupplychain/blob/master/license.go

// LicenseFilePrefix is a list of filename prefixes that indicate it
//  might contain a software license
var LicenseFilePrefix = []string{
	"licence", // UK spelling
	"license", // US spelling
	"copying",
	"unlicense",
	"copyright",
	"copyleft",
}

// LegalFileSubstring are substrings that indicate the file is likely
// to contain some type of legal declaration.  "legal" is often used
// that it might moved to LicenseFilePrefix
var LegalFileSubstring = []string{
	"legal",
	"notice",
	"disclaimer",
	"patent",
	"third-party",
	"thirdparty",
}

// IsLegalFile returns true if the file is likely to contain some type
// of of legal declaration or licensing information
func IsLegalFile(path string) bool {
	lowerfile := strings.ToLower(filepath.Base(path))
	for _, prefix := range LicenseFilePrefix {
		if strings.HasPrefix(lowerfile, prefix) {
			return true
		}
	}
	for _, substring := range LegalFileSubstring {
		if strings.Index(lowerfile, substring) != -1 {
			return true
		}
	}
	return false
}
<commit_msg>gvc: do not keep test files if they contain legal words<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/Masterminds/glide/cfg"
	gpath "github.com/Masterminds/glide/path"
	"github.com/bmatcuk/doublestar"
	"github.com/spf13/cobra"
)

var cmd = &cobra.Command{
	Use:   "glide-vc",
	Short: "glide vendor cleaner",
	Run:   glidevc,
}

type options struct {
	dryrun       bool
	onlyCode     bool
	noTests      bool
	noLegalFiles bool
	keepPatterns []string
}

var (
	opts         options
	codeSuffixes = []string{".go", ".c", ".s", ".S", ".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"}
)

const (
	goTestSuffix = "_test.go"
)

func init() {
	cmd.PersistentFlags().BoolVar(&opts.dryrun, "dryrun", false, "just output what will be removed")
	cmd.PersistentFlags().BoolVar(&opts.onlyCode, "only-code", false, "keep only go files (including go test files)")
	cmd.PersistentFlags().BoolVar(&opts.noTests, "no-tests", false, "remove also go test files (requires --only-code)")
	cmd.PersistentFlags().BoolVar(&opts.noLegalFiles, "no-legal-files", false, "remove also licenses and legal files")
	cmd.PersistentFlags().StringSliceVar(&opts.keepPatterns, "keep", []string{}, "A pattern to keep additional files inside needed packages. The pattern match will be relative to the deeper vendor dir. Supports double star (**) patterns. (see https://golang.org/pkg/path/filepath/#Match and https://github.com/bmatcuk/doublestar). Can be specified multiple times. For example to keep all the files with json extension use the '**/*.json' pattern.")
}

func main() {
	cmd.Execute()
}

func glidevc(cmd *cobra.Command, args []string) {
	if opts.noTests && !opts.onlyCode {
		fmt.Printf("--no-tests requires --only-code")
		os.Exit(1)
	}

	if err := cleanup(".", opts); err != nil {
		fmt.Print(err)
		os.Exit(1)
	}
	return
}

func cleanup(path string, opts options) error {
	lock, err := LoadGlideLockfile(path)
	if err != nil {
		return fmt.Errorf("Could not load lockfile: %v", err)
	}

	// The package list already have the path converted to the os specific
	// path separator, needed for future comparisons.
	pkgList := []string{}
	repoList := []string{}
	// TODO(sgotti) Should we also consider devImports?
	for _, imp := range lock.Imports {
		// This converts pkg separator "/" to os specific separator
		repoList = append(repoList, filepath.FromSlash(imp.Name))

		if len(imp.Subpackages) > 0 {
			for _, sp := range imp.Subpackages {
				// This converts pkg separator "/" to os specific separator
				pkgList = append(pkgList, filepath.Join(imp.Name, sp))
			}
		}
		// TODO(sgotti) we cannot skip the base import if it has subpackages
		// because glide doesn't write "." as a subpackage, otherwise if some
		// files in the base import are needed they will be removed.

		// This converts pkg separator "/" to os specific separator
		pkgList = append(pkgList, filepath.FromSlash(imp.Name))
	}

	vpath, err := gpath.Vendor()
	if err != nil {
		return err
	}
	if vpath == "" {
		return fmt.Errorf("cannot fine vendor dir")
	}

	type pathData struct {
		path  string
		isDir bool
	}
	var searchPath string
	markForKeep := map[string]pathData{}
	markForDelete := []pathData{}

	// Walk vendor directory
	searchPath = vpath + string(os.PathSeparator)
	err = filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if path == searchPath || path == vpath {
			return nil
		}

		localPath := strings.TrimPrefix(path, searchPath)

		lastVendorPath, err := getLastVendorPath(localPath)
		if err != nil {
			return err
		}
		if lastVendorPath == "" {
			lastVendorPath = localPath
		}

		keep := false
		for _, name := range pkgList {
			// If the file's parent directory is a needed package, keep it.
			if !info.IsDir() && filepath.Dir(lastVendorPath) == name {
				if opts.onlyCode {
					if opts.noTests && strings.HasSuffix(path, "_test.go") {
						keep = false
						continue
					}
					for _, suffix := range codeSuffixes {
						if strings.HasSuffix(path, suffix) {
							keep = true
						}
					}
					// Match keep patterns
					for _, keepPattern := range opts.keepPatterns {
						ok, err := doublestar.Match(keepPattern, lastVendorPath)
						// TODO(sgotti) if a bad pattern is encountered stop here. Actually there's no function to verify a pattern before using it, perhaps just a fake match at the start will work.
						if err != nil {
							return fmt.Errorf("bad pattern: %q", keepPattern)
						}
						if ok {
							keep = true
						}
					}
				} else {
					keep = true
				}
			}
		}

		// Keep all the legal files inside top repo dir and required package dirs
		for _, name := range append(repoList, pkgList...) {
			if !info.IsDir() && filepath.Dir(lastVendorPath) == name {
				if !opts.noLegalFiles {
					if IsLegalFile(path) {
						keep = true
					}
				}
			}
		}

		// If a directory is a needed package then keep it
		if keep == false && info.IsDir() {
			for _, name := range pkgList {
				if name == lastVendorPath {
					keep = true
				}
			}
		}

		if keep {
			// Keep also all parents of current path
			curpath := localPath
			for {
				curpath = filepath.Dir(curpath)
				if curpath == "." {
					break
				}
				if _, ok := markForKeep[curpath]; ok {
					// Already marked for keep
					break
				}
				markForKeep[curpath] = pathData{curpath, true}
			}

			// Mark for keep
			markForKeep[localPath] = pathData{localPath, info.IsDir()}
		}

		return nil
	})
	if err != nil {
		return err
	}

	// Generate deletion list
	err = filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			// Ignore not existant files due to previous removal of the parent directory
			if !os.IsNotExist(err) {
				return err
			}
		}
		localPath := strings.TrimPrefix(path, searchPath)
		if localPath == "" {
			return nil
		}
		if _, ok := markForKeep[localPath]; !ok {
			markForDelete = append(markForDelete, pathData{path, info.IsDir()})
			if info.IsDir() {
				// skip directory contents since it has been marked for removal
				return filepath.SkipDir
			}
		}
		return nil
	})
	if err != nil {
		return err
	}

	// Perform the actual delete.
	for _, marked := range markForDelete {
		localPath := strings.TrimPrefix(marked.path, searchPath)
		if marked.isDir {
			fmt.Printf("Removing unused dir: %s\n", localPath)
		} else {
			fmt.Printf("Removing unused file: %s\n", localPath)
		}
		if !opts.dryrun {
			rerr := os.RemoveAll(marked.path)
			if rerr != nil {
				return rerr
			}
		}
	}

	return nil
}

func getLastVendorPath(path string) (string, error) {
	curpath := path
	for {
		if curpath == "." {
			return "", nil
		}
		if filepath.Base(curpath) == "vendor" {
			return filepath.Rel(curpath, path)
		}
		curpath = filepath.Dir(curpath)
	}
}

// LoadLockfile loads the contents of a glide.lock file.
func LoadGlideLockfile(base string) (*cfg.Lockfile, error) {
	yml, err := ioutil.ReadFile(filepath.Join(base, gpath.LockFile))
	if err != nil {
		return nil, err
	}
	lock, err := cfg.LockfileFromYaml(yml)
	if err != nil {
		return nil, err
	}

	return lock, nil
}

// File lists and code took from https://github.com/client9/gosupplychain/blob/master/license.go

// LicenseFilePrefix is a list of filename prefixes that indicate it
//  might contain a software license
var LicenseFilePrefix = []string{
	"licence", // UK spelling
	"license", // US spelling
	"copying",
	"unlicense",
	"copyright",
	"copyleft",
}

// LegalFileSubstring are substrings that indicate the file is likely
// to contain some type of legal declaration.  "legal" is often used
// that it might be moved to LicenseFilePrefix
var LegalFileSubstring = []string{
	"legal",
	"notice",
	"disclaimer",
	"patent",
	"third-party",
	"thirdparty",
}

// IsLegalFile returns true if the file is likely to contain some type
// of of legal declaration or licensing information
func IsLegalFile(path string) bool {
	lowerfile := strings.ToLower(filepath.Base(path))
	for _, prefix := range LicenseFilePrefix {
		if strings.HasPrefix(lowerfile, prefix) && !strings.HasSuffix(lowerfile, goTestSuffix) {
			return true
		}
	}
	for _, substring := range LegalFileSubstring {
		if strings.Index(lowerfile, substring) != -1 && !strings.HasSuffix(lowerfile, goTestSuffix) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/gorilla/context"
	"github.com/gorilla/sessions"
	"github.com/maiah/han.go/components/auth"
	"html/template"
	"log"
	"net/http"
)

var (
	store = sessions.NewCookieStore([]byte("smallelephantandbigfly"))
)

func main() {
	// Setup route handlers
	http.HandleFunc("/", index)
	http.HandleFunc("/login", login)
	http.HandleFunc("/logout", logout)
	http.HandleFunc("/home", home)
	http.HandleFunc("/settings", settings)

	// Start the server
	log.Println("Listening on port 5000")
	log.Fatal(http.ListenAndServe(":5000",
		context.ClearHandler(http.DefaultServeMux)))
}

// Route Handlers

func index(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path

	if len(path) >= 7 {
		path = r.URL.Path[0:7]
	}

	if path == "/public" {
		public(w, r) // invoke static file handler
	} else {
		fmt.Fprintf(w, "welcome my han.go")
	}
}

// Custom static file handling

func public(w http.ResponseWriter, r *http.Request) {
	file := r.URL.Path[1:]
	http.ServeFile(w, r, file)
}

type page struct {
	Message string
}

func login(w http.ResponseWriter, r *http.Request) {
	if auth.IsAuthorized(store, r) {
		http.Redirect(w, r, "/home", http.StatusFound)

	} else {
		loginPage := "pages/login.html"

		if r.Method == "GET" {
			t, _ := template.ParseFiles(loginPage)
			t.Execute(w, nil)

		} else if r.Method == "POST" {
			if auth.IsAuthenticated(store, w, r) {
				http.Redirect(w, r, "/home", http.StatusFound)

			} else {
				t, err := template.ParseFiles(loginPage)

				if err != nil {
					log.Fatal(err)
				}

				if t.Execute(w,
					page{Message: "Invalid username/password"}) != nil {
					log.Fatal(err)
				}
			}
		}
	}
}

func logout(w http.ResponseWriter, r *http.Request) {
	session, _ := store.Get(r, "user-session")
	session.Options = &sessions.Options{MaxAge: -1}
	session.Values = nil
	session.Save(r, w)

	http.Redirect(w, r, "/login", http.StatusFound)
}

func home(w http.ResponseWriter, r *http.Request) {
	if auth.IsAuthorized(store, r) {
		homePage := "pages/home.html"
		t, err := template.ParseFiles(homePage)
		if err != nil {
			log.Fatal(err)
		}

		session, _ := store.Get(r, "user-session")

		t.Execute(w, page{Message: session.Values["username"].(string)})

	} else {
		http.Redirect(w, r, "/login", http.StatusFound)
	}
}

func settings(w http.ResponseWriter, r *http.Request) {
	if auth.IsAuthorized(store, r, "ADMIN") {
		file := "pages/settings.html"
		http.ServeFile(w, r, file)

	} else {
		http.Redirect(w, r, "/login", http.StatusFound)
	}
}
<commit_msg>Use port from env variable<commit_after>package main

import (
	"fmt"
	"github.com/gorilla/context"
	"github.com/gorilla/sessions"
	"github.com/maiah/han.go/components/auth"
	"html/template"
	"log"
	"net/http"
	"os"
)

var (
	store = sessions.NewCookieStore([]byte("smallelephantandbigfly"))
)

func main() {
	// Setup route handlers
	http.HandleFunc("/", index)
	http.HandleFunc("/login", login)
	http.HandleFunc("/logout", logout)
	http.HandleFunc("/home", home)
	http.HandleFunc("/settings", settings)

	port := os.Getenv("PORT")

	// Start the server
	log.Println("Listening on port " + port)
	log.Fatal(http.ListenAndServe(":"+port,
		context.ClearHandler(http.DefaultServeMux)))
}

// Route Handlers

func index(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path

	if len(path) >= 7 {
		path = r.URL.Path[0:7]
	}

	if path == "/public" {
		public(w, r) // invoke static file handler
	} else {
		fmt.Fprintf(w, "welcome my han.go")
	}
}

// Custom static file handling

func public(w http.ResponseWriter, r *http.Request) {
	file := r.URL.Path[1:]
	http.ServeFile(w, r, file)
}

type page struct {
	Message string
}

func login(w http.ResponseWriter, r *http.Request) {
	if auth.IsAuthorized(store, r) {
		http.Redirect(w, r, "/home", http.StatusFound)

	} else {
		loginPage := "pages/login.html"

		if r.Method == "GET" {
			t, _ := template.ParseFiles(loginPage)
			t.Execute(w, nil)

		} else if r.Method == "POST" {
			if auth.IsAuthenticated(store, w, r) {
				http.Redirect(w, r, "/home", http.StatusFound)

			} else {
				t, err := template.ParseFiles(loginPage)

				if err != nil {
					log.Fatal(err)
				}

				if t.Execute(w,
					page{Message: "Invalid username/password"}) != nil {
					log.Fatal(err)
				}
			}
		}
	}
}

func logout(w http.ResponseWriter, r *http.Request) {
	session, _ := store.Get(r, "user-session")
	session.Options = &sessions.Options{MaxAge: -1}
	session.Values = nil
	session.Save(r, w)

	http.Redirect(w, r, "/login", http.StatusFound)
}

func home(w http.ResponseWriter, r *http.Request) {
	if auth.IsAuthorized(store, r) {
		homePage := "pages/home.html"
		t, err := template.ParseFiles(homePage)
		if err != nil {
			log.Fatal(err)
		}

		session, _ := store.Get(r, "user-session")

		t.Execute(w, page{Message: session.Values["username"].(string)})

	} else {
		http.Redirect(w, r, "/login", http.StatusFound)
	}
}

func settings(w http.ResponseWriter, r *http.Request) {
	if auth.IsAuthorized(store, r, "ADMIN") {
		file := "pages/settings.html"
		http.ServeFile(w, r, file)

	} else {
		http.Redirect(w, r, "/login", http.StatusFound)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go.net/html"
	"code.google.com/p/go.net/html/atom"

	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"regexp"
	"strings"
)

var barMap = map[string]string{}

type beerInfo struct {
	brewery string
	brew    string
}

func findBeer(node *html.Node, beers *[]beerInfo) {
	if node.DataAtom == atom.Div {
		for _, attr := range node.Attr {
			if attr.Key == "id" && strings.HasPrefix(attr.Val, "beer-") {
				brewery, brew := "", ""
				findBrewery(node, &brewery)
				findBrew(node, &brew)
				*beers = append(*beers, beerInfo{brewery, brew})
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		findBeer(kid, beers)
	}
}

func findBrewery(node *html.Node, brewery *string) bool {
	if node.DataAtom == atom.H4 {
		if content := node.FirstChild; content != nil {
			*brewery = content.Data
			return true
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if findBrewery(kid, brewery) {
			return true
		}
	}
	return false
}

func findBrew(node *html.Node, brew *string) bool {
	for _, attr := range node.Attr {
		if attr.Key == "class" && attr.Val == "beer-name" {
			if content := node.FirstChild; content != nil {
				*brew = content.Data
				return true
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if findBrew(kid, brew) {
			return true
		}
	}
	return false
}

func checkId(id string) bool {
	ok, err := regexp.MatchString("^[[:xdigit:]]{24}$", id)
	if err != nil {
		return false
	}
	return ok
}

func readRc() {
	usr, err := user.Current()
	if err != nil {
		return
	}
	data, err := ioutil.ReadFile(usr.HomeDir + "/.taplistrc")
	if err != nil {
		return
	}
	lines := strings.Split(string(data), "\n")
	for _, line := range lines {
		line = strings.TrimSpace(line)
		idx := strings.IndexAny(line, " \t")
		if idx < len(line) - 1 {
			id, name := line[:idx], strings.TrimSpace(line[idx:])
			barMap[id] = name
		}
	}
}

func findBar(arg string) (string, string) {
	for id, name := range barMap {
		if strings.Contains(strings.ToLower(name), strings.ToLower(arg)) {
			return id, name
		}
	}
	return "", ""
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("taplist: ")

	if len(os.Args) != 2 {
		log.Fatalln("usage: taplist <id>")
	}
	readRc()
	arg := strings.ToLower(os.Args[1])
	id, name := "", ""
	if checkId(arg) {
		id, name = arg, arg
	} else {
		id, name = findBar(arg)
	}
	if id == "" {
		log.Fatalln(arg + " doesn't look like a valid name or taplister bar id")
	}

	resp, err := http.Get("http://www.taplister.com/bars/" + id)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	doc, err := html.Parse(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	beers := []beerInfo{}
	findBeer(doc, &beers)
	fmt.Println("On tap at " + name + ":\n")
	for _, beer := range beers {
		fmt.Printf("%-38.38s  %s\n", beer.brewery, beer.brew)
	}
}
<commit_msg>Updated usage for new name option.<commit_after>package main

import (
	"code.google.com/p/go.net/html"
	"code.google.com/p/go.net/html/atom"

	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"regexp"
	"strings"
)

var barMap = map[string]string{}

type beerInfo struct {
	brewery string
	brew    string
}

func findBeer(node *html.Node, beers *[]beerInfo) {
	if node.DataAtom == atom.Div {
		for _, attr := range node.Attr {
			if attr.Key == "id" && strings.HasPrefix(attr.Val, "beer-") {
				brewery, brew := "", ""
				findBrewery(node, &brewery)
				findBrew(node, &brew)
				*beers = append(*beers, beerInfo{brewery, brew})
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		findBeer(kid, beers)
	}
}

func findBrewery(node *html.Node, brewery *string) bool {
	if node.DataAtom == atom.H4 {
		if content := node.FirstChild; content != nil {
			*brewery = content.Data
			return true
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if findBrewery(kid, brewery) {
			return true
		}
	}
	return false
}

func findBrew(node *html.Node, brew *string) bool {
	for _, attr := range node.Attr {
		if attr.Key == "class" && attr.Val == "beer-name" {
			if content := node.FirstChild; content != nil {
				*brew = content.Data
				return true
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if findBrew(kid, brew) {
			return true
		}
	}
	return false
}

func checkId(id string) bool {
	ok, err := regexp.MatchString("^[[:xdigit:]]{24}$", id)
	if err != nil {
		return false
	}
	return ok
}

func readRc() {
	usr, err := user.Current()
	if err != nil {
		return
	}
	data, err := ioutil.ReadFile(usr.HomeDir + "/.taplistrc")
	if err != nil {
		return
	}
	lines := strings.Split(string(data), "\n")
	for _, line := range lines {
		line = strings.TrimSpace(line)
		idx := strings.IndexAny(line, " \t")
		if idx < len(line) - 1 {
			id, name := line[:idx], strings.TrimSpace(line[idx:])
			barMap[id] = name
		}
	}
}

func findBar(arg string) (string, string) {
	for id, name := range barMap {
		if strings.Contains(strings.ToLower(name), strings.ToLower(arg)) {
			return id, name
		}
	}
	return "", ""
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("taplist: ")

	if len(os.Args) != 2 {
		log.Fatalln("usage: taplist <id> | <name>")
	}
	readRc()
	arg := strings.ToLower(os.Args[1])
	id, name := "", ""
	if checkId(arg) {
		id, name = arg, arg
	} else {
		id, name = findBar(arg)
	}
	if id == "" {
		log.Fatalln(arg + " doesn't look like a valid name or taplister bar id")
	}

	resp, err := http.Get("http://www.taplister.com/bars/" + id)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	doc, err := html.Parse(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	beers := []beerInfo{}
	findBeer(doc, &beers)
	fmt.Println("On tap at " + name + ":\n")
	for _, beer := range beers {
		fmt.Printf("%-38.38s  %s\n", beer.brewery, beer.brew)
	}
}
<|endoftext|>


<commit_before>package google

import (
	"fmt"
	"reflect"

	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
	computeBeta "google.golang.org/api/compute/v0.beta"
	"google.golang.org/api/googleapi"
)

func instanceSchedulingNodeAffinitiesElemSchema() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"key": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"operator": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringInSlice([]string{"IN", "NOT_IN"}, false),
			},
			"values": {
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
		},
	}
}

func expandAliasIpRanges(ranges []interface{}) []*computeBeta.AliasIpRange {
	ipRanges := make([]*computeBeta.AliasIpRange, 0, len(ranges))
	for _, raw := range ranges {
		data := raw.(map[string]interface{})
		ipRanges = append(ipRanges, &computeBeta.AliasIpRange{
			IpCidrRange:         data["ip_cidr_range"].(string),
			SubnetworkRangeName: data["subnetwork_range_name"].(string),
		})
	}
	return ipRanges
}

func flattenAliasIpRange(ranges []*computeBeta.AliasIpRange) []map[string]interface{} {
	rangesSchema := make([]map[string]interface{}, 0, len(ranges))
	for _, ipRange := range ranges {
		rangesSchema = append(rangesSchema, map[string]interface{}{
			"ip_cidr_range":         ipRange.IpCidrRange,
			"subnetwork_range_name": ipRange.SubnetworkRangeName,
		})
	}
	return rangesSchema
}

func expandScheduling(v interface{}) (*computeBeta.Scheduling, error) {
	if v == nil {
		// We can't set default values for lists.
		return &computeBeta.Scheduling{
			AutomaticRestart: googleapi.Bool(true),
		}, nil
	}

	ls := v.([]interface{})
	if len(ls) == 0 {
		// We can't set default values for lists
		return &computeBeta.Scheduling{
			AutomaticRestart: googleapi.Bool(true),
		}, nil
	}

	if len(ls) > 1 || ls[0] == nil {
		return nil, fmt.Errorf("expected exactly one scheduling block")
	}

	original := ls[0].(map[string]interface{})
	scheduling := &computeBeta.Scheduling{
		ForceSendFields: make([]string, 0, 4),
	}

	if v, ok := original["automatic_restart"]; ok {
		scheduling.AutomaticRestart = googleapi.Bool(v.(bool))
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "AutomaticRestart")
	}

	if v, ok := original["preemptible"]; ok {
		scheduling.Preemptible = v.(bool)
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "Preemptible")

	}

	if v, ok := original["on_host_maintenance"]; ok {
		scheduling.OnHostMaintenance = v.(string)
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "OnHostMaintenance")
	}

	if v, ok := original["node_affinities"]; ok && v != nil {
		naSet := v.(*schema.Set).List()
		scheduling.NodeAffinities = make([]*computeBeta.SchedulingNodeAffinity, len(ls))
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "NodeAffinities")
		for _, nodeAffRaw := range naSet {
			if nodeAffRaw == nil {
				continue
			}
			nodeAff := nodeAffRaw.(map[string]interface{})
			transformed := &computeBeta.SchedulingNodeAffinity{
				Key:      nodeAff["key"].(string),
				Operator: nodeAff["operator"].(string),
				Values:   convertStringArr(nodeAff["values"].(*schema.Set).List()),
			}
			scheduling.NodeAffinities = append(scheduling.NodeAffinities, transformed)
		}
	}

	return scheduling, nil
}

func flattenScheduling(resp *computeBeta.Scheduling) []map[string]interface{} {
	schedulingMap := map[string]interface{}{
		"on_host_maintenance": resp.OnHostMaintenance,
		"preemptible":         resp.Preemptible,
	}

	if resp.AutomaticRestart != nil {
		schedulingMap["automatic_restart"] = *resp.AutomaticRestart
	}

	nodeAffinities := schema.NewSet(schema.HashResource(instanceSchedulingNodeAffinitiesElemSchema()), nil)
	for _, na := range resp.NodeAffinities {
		nodeAffinities.Add(map[string]interface{}{
			"key":      na.Key,
			"operator": na.Operator,
			"values":   schema.NewSet(schema.HashString, convertStringArrToInterface(na.Values)),
		})
	}
	schedulingMap["node_affinities"] = nodeAffinities

	return []map[string]interface{}{schedulingMap}
}

func flattenAccessConfigs(accessConfigs []*computeBeta.AccessConfig) ([]map[string]interface{}, string) {
	flattened := make([]map[string]interface{}, len(accessConfigs))
	natIP := ""
	for i, ac := range accessConfigs {
		flattened[i] = map[string]interface{}{
			"nat_ip":       ac.NatIP,
			"network_tier": ac.NetworkTier,
		}
		if ac.SetPublicPtr {
			flattened[i]["public_ptr_domain_name"] = ac.PublicPtrDomainName
		}
		if natIP == "" {
			natIP = ac.NatIP
		}
	}
	return flattened, natIP
}

func flattenNetworkInterfaces(d *schema.ResourceData, config *Config, networkInterfaces []*computeBeta.NetworkInterface) ([]map[string]interface{}, string, string, string, error) {
	flattened := make([]map[string]interface{}, len(networkInterfaces))
	var region, internalIP, externalIP string

	for i, iface := range networkInterfaces {
		var ac []map[string]interface{}
		ac, externalIP = flattenAccessConfigs(iface.AccessConfigs)

		subnet, err := ParseSubnetworkFieldValue(iface.Subnetwork, d, config)
		if err != nil {
			return nil, "", "", "", err
		}
		region = subnet.Region

		flattened[i] = map[string]interface{}{
			"network_ip":         iface.NetworkIP,
			"network":            ConvertSelfLinkToV1(iface.Network),
			"subnetwork":         ConvertSelfLinkToV1(iface.Subnetwork),
			"subnetwork_project": subnet.Project,
			"access_config":      ac,
			"alias_ip_range":     flattenAliasIpRange(iface.AliasIpRanges),
		}
		// Instance template interfaces never have names, so they're absent
		// in the instance template network_interface schema. We want to use the
		// same flattening code for both resource types, so we avoid trying to
		// set the name field when it's not set at the GCE end.
		if iface.Name != "" {
			flattened[i]["name"] = iface.Name
		}
		if internalIP == "" {
			internalIP = iface.NetworkIP
		}
	}
	return flattened, region, internalIP, externalIP, nil
}

func expandAccessConfigs(configs []interface{}) []*computeBeta.AccessConfig {
	acs := make([]*computeBeta.AccessConfig, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})
		acs[i] = &computeBeta.AccessConfig{
			Type:        "ONE_TO_ONE_NAT",
			NatIP:       data["nat_ip"].(string),
			NetworkTier: data["network_tier"].(string),
		}
		if ptr, ok := data["public_ptr_domain_name"]; ok && ptr != "" {
			acs[i].SetPublicPtr = true
			acs[i].PublicPtrDomainName = ptr.(string)
		}
	}
	return acs
}

func expandNetworkInterfaces(d TerraformResourceData, config *Config) ([]*computeBeta.NetworkInterface, error) {
	configs := d.Get("network_interface").([]interface{})
	ifaces := make([]*computeBeta.NetworkInterface, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})

		network := data["network"].(string)
		subnetwork := data["subnetwork"].(string)
		if network == "" && subnetwork == "" {
			return nil, fmt.Errorf("exactly one of network or subnetwork must be provided")
		}

		nf, err := ParseNetworkFieldValue(network, d, config)
		if err != nil {
			return nil, fmt.Errorf("cannot determine self_link for network %q: %s", network, err)
		}

		subnetProjectField := fmt.Sprintf("network_interface.%d.subnetwork_project", i)
		sf, err := ParseSubnetworkFieldValueWithProjectField(subnetwork, subnetProjectField, d, config)
		if err != nil {
			return nil, fmt.Errorf("cannot determine self_link for subnetwork %q: %s", subnetwork, err)
		}

		ifaces[i] = &computeBeta.NetworkInterface{
			NetworkIP:     data["network_ip"].(string),
			Network:       nf.RelativeLink(),
			Subnetwork:    sf.RelativeLink(),
			AccessConfigs: expandAccessConfigs(data["access_config"].([]interface{})),
			AliasIpRanges: expandAliasIpRanges(data["alias_ip_range"].([]interface{})),
		}

	}
	return ifaces, nil
}

func flattenServiceAccounts(serviceAccounts []*computeBeta.ServiceAccount) []map[string]interface{} {
	result := make([]map[string]interface{}, len(serviceAccounts))
	for i, serviceAccount := range serviceAccounts {
		result[i] = map[string]interface{}{
			"email":  serviceAccount.Email,
			"scopes": schema.NewSet(stringScopeHashcode, convertStringArrToInterface(serviceAccount.Scopes)),
		}
	}
	return result
}

func expandServiceAccounts(configs []interface{}) []*computeBeta.ServiceAccount {
	accounts := make([]*computeBeta.ServiceAccount, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})

		accounts[i] = &computeBeta.ServiceAccount{
			Email:  data["email"].(string),
			Scopes: canonicalizeServiceScopes(convertStringSet(data["scopes"].(*schema.Set))),
		}

		if accounts[i].Email == "" {
			accounts[i].Email = "default"
		}
	}
	return accounts
}

func flattenGuestAccelerators(accelerators []*computeBeta.AcceleratorConfig) []map[string]interface{} {
	acceleratorsSchema := make([]map[string]interface{}, len(accelerators))
	for i, accelerator := range accelerators {
		acceleratorsSchema[i] = map[string]interface{}{
			"count": accelerator.AcceleratorCount,
			"type":  accelerator.AcceleratorType,
		}
	}
	return acceleratorsSchema
}

func resourceInstanceTags(d TerraformResourceData) *computeBeta.Tags {
	// Calculate the tags
	var tags *computeBeta.Tags
	if v := d.Get("tags"); v != nil {
		vs := v.(*schema.Set)
		tags = new(computeBeta.Tags)
		tags.Items = make([]string, vs.Len())
		for i, v := range vs.List() {
			tags.Items[i] = v.(string)
		}

		tags.Fingerprint = d.Get("tags_fingerprint").(string)
	}

	return tags
}

func expandShieldedVmConfigs(d TerraformResourceData) *computeBeta.ShieldedVmConfig {
	if _, ok := d.GetOk("shielded_instance_config"); !ok {
		return nil
	}

	prefix := "shielded_instance_config.0"
	return &computeBeta.ShieldedVmConfig{
		EnableSecureBoot:          d.Get(prefix + ".enable_secure_boot").(bool),
		EnableVtpm:                d.Get(prefix + ".enable_vtpm").(bool),
		EnableIntegrityMonitoring: d.Get(prefix + ".enable_integrity_monitoring").(bool),
		ForceSendFields:           []string{"EnableSecureBoot", "EnableVtpm", "EnableIntegrityMonitoring"},
	}
}

func flattenShieldedVmConfig(shieldedVmConfig *computeBeta.ShieldedVmConfig) []map[string]bool {
	if shieldedVmConfig == nil {
		return nil
	}

	return []map[string]bool{{
		"enable_secure_boot":          shieldedVmConfig.EnableSecureBoot,
		"enable_vtpm":                 shieldedVmConfig.EnableVtpm,
		"enable_integrity_monitoring": shieldedVmConfig.EnableIntegrityMonitoring,
	}}
}

func expandDisplayDevice(d TerraformResourceData) *computeBeta.DisplayDevice {
	if _, ok := d.GetOk("enable_display"); !ok {
		return nil
	}
	return &computeBeta.DisplayDevice{
		EnableDisplay:   d.Get("enable_display").(bool),
		ForceSendFields: []string{"EnableDisplay"},
	}
}

func flattenEnableDisplay(displayDevice *computeBeta.DisplayDevice) interface{} {
	if displayDevice == nil {
		return nil
	}

	return displayDevice.EnableDisplay
}

// Terraform doesn't correctly calculate changes on schema.Set, so we do it manually
// https://github.com/hashicorp/terraform-plugin-sdk/issues/98
func schedulingHasChange(d *schema.ResourceData) bool {
	if !d.HasChange("scheduling") {
		// This doesn't work correctly, which is why this method exists
		// But it is here for posterity
		return false
	}
	o, n := d.GetChange("scheduling")
	oScheduling := o.([]interface{})[0].(map[string]interface{})
	newScheduling := n.([]interface{})[0].(map[string]interface{})
	originalNa := oScheduling["node_affinities"].(*schema.Set)
	newNa := newScheduling["node_affinities"].(*schema.Set)
	if oScheduling["automatic_restart"] != newScheduling["automatic_restart"] {
		return true
	}

	if oScheduling["preemptible"] != newScheduling["preemptible"] {
		return true
	}

	if oScheduling["on_host_maintenance"] != newScheduling["on_host_maintenance"] {
		return true
	}

	return reflect.DeepEqual(newNa, originalNa)
}
<commit_msg>add min_node_cpus to compute_instance.scheduling (#3634) (#466)<commit_after>// <% autogen_exception -%>

package google

import (
	"fmt"
	"reflect"

	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
	computeBeta "google.golang.org/api/compute/v0.beta"
	"google.golang.org/api/googleapi"
)

func instanceSchedulingNodeAffinitiesElemSchema() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"key": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"operator": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringInSlice([]string{"IN", "NOT_IN"}, false),
			},
			"values": {
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
		},
	}
}

func expandAliasIpRanges(ranges []interface{}) []*computeBeta.AliasIpRange {
	ipRanges := make([]*computeBeta.AliasIpRange, 0, len(ranges))
	for _, raw := range ranges {
		data := raw.(map[string]interface{})
		ipRanges = append(ipRanges, &computeBeta.AliasIpRange{
			IpCidrRange:         data["ip_cidr_range"].(string),
			SubnetworkRangeName: data["subnetwork_range_name"].(string),
		})
	}
	return ipRanges
}

func flattenAliasIpRange(ranges []*computeBeta.AliasIpRange) []map[string]interface{} {
	rangesSchema := make([]map[string]interface{}, 0, len(ranges))
	for _, ipRange := range ranges {
		rangesSchema = append(rangesSchema, map[string]interface{}{
			"ip_cidr_range":         ipRange.IpCidrRange,
			"subnetwork_range_name": ipRange.SubnetworkRangeName,
		})
	}
	return rangesSchema
}

func expandScheduling(v interface{}) (*computeBeta.Scheduling, error) {
	if v == nil {
		// We can't set default values for lists.
		return &computeBeta.Scheduling{
			AutomaticRestart: googleapi.Bool(true),
		}, nil
	}

	ls := v.([]interface{})
	if len(ls) == 0 {
		// We can't set default values for lists
		return &computeBeta.Scheduling{
			AutomaticRestart: googleapi.Bool(true),
		}, nil
	}

	if len(ls) > 1 || ls[0] == nil {
		return nil, fmt.Errorf("expected exactly one scheduling block")
	}

	original := ls[0].(map[string]interface{})
	scheduling := &computeBeta.Scheduling{
		ForceSendFields: make([]string, 0, 4),
	}

	if v, ok := original["automatic_restart"]; ok {
		scheduling.AutomaticRestart = googleapi.Bool(v.(bool))
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "AutomaticRestart")
	}

	if v, ok := original["preemptible"]; ok {
		scheduling.Preemptible = v.(bool)
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "Preemptible")
	}

	if v, ok := original["on_host_maintenance"]; ok {
		scheduling.OnHostMaintenance = v.(string)
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "OnHostMaintenance")
	}

	if v, ok := original["node_affinities"]; ok && v != nil {
		naSet := v.(*schema.Set).List()
		scheduling.NodeAffinities = make([]*computeBeta.SchedulingNodeAffinity, len(ls))
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "NodeAffinities")
		for _, nodeAffRaw := range naSet {
			if nodeAffRaw == nil {
				continue
			}
			nodeAff := nodeAffRaw.(map[string]interface{})
			transformed := &computeBeta.SchedulingNodeAffinity{
				Key:      nodeAff["key"].(string),
				Operator: nodeAff["operator"].(string),
				Values:   convertStringArr(nodeAff["values"].(*schema.Set).List()),
			}
			scheduling.NodeAffinities = append(scheduling.NodeAffinities, transformed)
		}
	}

<% unless version == 'ga' -%>
	if v, ok := original["min_node_cpus"]; ok {
		scheduling.MinNodeCpus = int64(v.(int))
	}
<% end -%>

	return scheduling, nil
}

func flattenScheduling(resp *computeBeta.Scheduling) []map[string]interface{} {
	schedulingMap := map[string]interface{}{
		"on_host_maintenance": resp.OnHostMaintenance,
		"preemptible":         resp.Preemptible,
<% unless version == 'ga' -%>
		"min_node_cpus":       resp.MinNodeCpus,
<% end -%>
	}

	if resp.AutomaticRestart != nil {
		schedulingMap["automatic_restart"] = *resp.AutomaticRestart
	}

	nodeAffinities := schema.NewSet(schema.HashResource(instanceSchedulingNodeAffinitiesElemSchema()), nil)
	for _, na := range resp.NodeAffinities {
		nodeAffinities.Add(map[string]interface{}{
			"key":      na.Key,
			"operator": na.Operator,
			"values":   schema.NewSet(schema.HashString, convertStringArrToInterface(na.Values)),
		})
	}
	schedulingMap["node_affinities"] = nodeAffinities

	return []map[string]interface{}{schedulingMap}
}

func flattenAccessConfigs(accessConfigs []*computeBeta.AccessConfig) ([]map[string]interface{}, string) {
	flattened := make([]map[string]interface{}, len(accessConfigs))
	natIP := ""
	for i, ac := range accessConfigs {
		flattened[i] = map[string]interface{}{
			"nat_ip":       ac.NatIP,
			"network_tier": ac.NetworkTier,
		}
		if ac.SetPublicPtr {
			flattened[i]["public_ptr_domain_name"] = ac.PublicPtrDomainName
		}
		if natIP == "" {
			natIP = ac.NatIP
		}
	}
	return flattened, natIP
}

func flattenNetworkInterfaces(d *schema.ResourceData, config *Config, networkInterfaces []*computeBeta.NetworkInterface) ([]map[string]interface{}, string, string, string, error) {
	flattened := make([]map[string]interface{}, len(networkInterfaces))
	var region, internalIP, externalIP string

	for i, iface := range networkInterfaces {
		var ac []map[string]interface{}
		ac, externalIP = flattenAccessConfigs(iface.AccessConfigs)

		subnet, err := ParseSubnetworkFieldValue(iface.Subnetwork, d, config)
		if err != nil {
			return nil, "", "", "", err
		}
		region = subnet.Region

		flattened[i] = map[string]interface{}{
			"network_ip":         iface.NetworkIP,
			"network":            ConvertSelfLinkToV1(iface.Network),
			"subnetwork":         ConvertSelfLinkToV1(iface.Subnetwork),
			"subnetwork_project": subnet.Project,
			"access_config":      ac,
			"alias_ip_range":     flattenAliasIpRange(iface.AliasIpRanges),
		}
		// Instance template interfaces never have names, so they're absent
		// in the instance template network_interface schema. We want to use the
		// same flattening code for both resource types, so we avoid trying to
		// set the name field when it's not set at the GCE end.
		if iface.Name != "" {
			flattened[i]["name"] = iface.Name
		}
		if internalIP == "" {
			internalIP = iface.NetworkIP
		}
	}
	return flattened, region, internalIP, externalIP, nil
}

func expandAccessConfigs(configs []interface{}) []*computeBeta.AccessConfig {
	acs := make([]*computeBeta.AccessConfig, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})
		acs[i] = &computeBeta.AccessConfig{
			Type:        "ONE_TO_ONE_NAT",
			NatIP:       data["nat_ip"].(string),
			NetworkTier: data["network_tier"].(string),
		}
		if ptr, ok := data["public_ptr_domain_name"]; ok && ptr != "" {
			acs[i].SetPublicPtr = true
			acs[i].PublicPtrDomainName = ptr.(string)
		}
	}
	return acs
}

func expandNetworkInterfaces(d TerraformResourceData, config *Config) ([]*computeBeta.NetworkInterface, error) {
	configs := d.Get("network_interface").([]interface{})
	ifaces := make([]*computeBeta.NetworkInterface, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})

		network := data["network"].(string)
		subnetwork := data["subnetwork"].(string)
		if network == "" && subnetwork == "" {
			return nil, fmt.Errorf("exactly one of network or subnetwork must be provided")
		}

		nf, err := ParseNetworkFieldValue(network, d, config)
		if err != nil {
			return nil, fmt.Errorf("cannot determine self_link for network %q: %s", network, err)
		}

		subnetProjectField := fmt.Sprintf("network_interface.%d.subnetwork_project", i)
		sf, err := ParseSubnetworkFieldValueWithProjectField(subnetwork, subnetProjectField, d, config)
		if err != nil {
			return nil, fmt.Errorf("cannot determine self_link for subnetwork %q: %s", subnetwork, err)
		}

		ifaces[i] = &computeBeta.NetworkInterface{
			NetworkIP:     data["network_ip"].(string),
			Network:       nf.RelativeLink(),
			Subnetwork:    sf.RelativeLink(),
			AccessConfigs: expandAccessConfigs(data["access_config"].([]interface{})),
			AliasIpRanges: expandAliasIpRanges(data["alias_ip_range"].([]interface{})),
		}

	}
	return ifaces, nil
}

func flattenServiceAccounts(serviceAccounts []*computeBeta.ServiceAccount) []map[string]interface{} {
	result := make([]map[string]interface{}, len(serviceAccounts))
	for i, serviceAccount := range serviceAccounts {
		result[i] = map[string]interface{}{
			"email":  serviceAccount.Email,
			"scopes": schema.NewSet(stringScopeHashcode, convertStringArrToInterface(serviceAccount.Scopes)),
		}
	}
	return result
}

func expandServiceAccounts(configs []interface{}) []*computeBeta.ServiceAccount {
	accounts := make([]*computeBeta.ServiceAccount, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})

		accounts[i] = &computeBeta.ServiceAccount{
			Email:  data["email"].(string),
			Scopes: canonicalizeServiceScopes(convertStringSet(data["scopes"].(*schema.Set))),
		}

		if accounts[i].Email == "" {
			accounts[i].Email = "default"
		}
	}
	return accounts
}

func flattenGuestAccelerators(accelerators []*computeBeta.AcceleratorConfig) []map[string]interface{} {
	acceleratorsSchema := make([]map[string]interface{}, len(accelerators))
	for i, accelerator := range accelerators {
		acceleratorsSchema[i] = map[string]interface{}{
			"count": accelerator.AcceleratorCount,
			"type":  accelerator.AcceleratorType,
		}
	}
	return acceleratorsSchema
}

func resourceInstanceTags(d TerraformResourceData) *computeBeta.Tags {
	// Calculate the tags
	var tags *computeBeta.Tags
	if v := d.Get("tags"); v != nil {
		vs := v.(*schema.Set)
		tags = new(computeBeta.Tags)
		tags.Items = make([]string, vs.Len())
		for i, v := range vs.List() {
			tags.Items[i] = v.(string)
		}

		tags.Fingerprint = d.Get("tags_fingerprint").(string)
	}

	return tags
}

func expandShieldedVmConfigs(d TerraformResourceData) *computeBeta.ShieldedVmConfig {
	if _, ok := d.GetOk("shielded_instance_config"); !ok {
		return nil
	}

	prefix := "shielded_instance_config.0"
	return &computeBeta.ShieldedVmConfig{
		EnableSecureBoot:          d.Get(prefix + ".enable_secure_boot").(bool),
		EnableVtpm:                d.Get(prefix + ".enable_vtpm").(bool),
		EnableIntegrityMonitoring: d.Get(prefix + ".enable_integrity_monitoring").(bool),
		ForceSendFields:           []string{"EnableSecureBoot", "EnableVtpm", "EnableIntegrityMonitoring"},
	}
}

func flattenShieldedVmConfig(shieldedVmConfig *computeBeta.ShieldedVmConfig) []map[string]bool {
	if shieldedVmConfig == nil {
		return nil
	}

	return []map[string]bool{{
		"enable_secure_boot":          shieldedVmConfig.EnableSecureBoot,
		"enable_vtpm":                 shieldedVmConfig.EnableVtpm,
		"enable_integrity_monitoring": shieldedVmConfig.EnableIntegrityMonitoring,
	}}
}

func expandDisplayDevice(d TerraformResourceData) *computeBeta.DisplayDevice {
	if _, ok := d.GetOk("enable_display"); !ok {
		return nil
	}
	return &computeBeta.DisplayDevice{
		EnableDisplay:   d.Get("enable_display").(bool),
		ForceSendFields: []string{"EnableDisplay"},
	}
}

func flattenEnableDisplay(displayDevice *computeBeta.DisplayDevice) interface{} {
	if displayDevice == nil {
		return nil
	}

	return displayDevice.EnableDisplay
}

// Terraform doesn't correctly calculate changes on schema.Set, so we do it manually
// https://github.com/hashicorp/terraform-plugin-sdk/issues/98
func schedulingHasChange(d *schema.ResourceData) bool {
	if !d.HasChange("scheduling") {
		// This doesn't work correctly, which is why this method exists
		// But it is here for posterity
		return false
	}
	o, n := d.GetChange("scheduling")
	oScheduling := o.([]interface{})[0].(map[string]interface{})
	newScheduling := n.([]interface{})[0].(map[string]interface{})
	originalNa := oScheduling["node_affinities"].(*schema.Set)
	newNa := newScheduling["node_affinities"].(*schema.Set)
	if oScheduling["automatic_restart"] != newScheduling["automatic_restart"] {
		return true
	}

	if oScheduling["preemptible"] != newScheduling["preemptible"] {
		return true
	}

	if oScheduling["on_host_maintenance"] != newScheduling["on_host_maintenance"] {
		return true
	}

<% unless version == 'ga' -%>
	if oScheduling["min_node_cpus"] != newScheduling["min_node_cpus"] {
		return true
	}
<% end -%>

	return reflect.DeepEqual(newNa, originalNa)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"syscall"
)

func main() {

	initFlags()

	// check permissions
	if os.Getuid() != 0 && os.Geteuid() != 0 {
		fmt.Printf("npserver-daemon should be run as root, have uid=%d and euid=%d\n", os.Getuid(), os.Geteuid())
		os.Exit(1)
	}

	// check start || sto
	if flags.Start == flags.Stop {
		fmt.Println("need --start or --stop flag")
	}

	if flags.Start {
		startDaemon()
	}
	if flags.Stop {
		stopDaemon()
	}

	// all good :)
}

func startDaemon() {
	// setup args for daemon call
	args := []string{
		"--name=npserver",
		"--noconfig",
		"--errlog=/var/log/npserver-daemon.log",
		"--output=/var/log/npserver.log",
		fmt.Sprintf("--pidfile=%s", flags.PIDFile),
		"--unsafe",
		"--",
		"/usr/local/bin/npserver",
	}

	// append extra args to args
	args = append(args, extraArgs...)

	// start process
	proc, err := os.StartProcess("daemon", args, &os.ProcAttr{
		Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
		Sys: &syscall.SysProcAttr{
			Credential: &syscall.Credential{
				Uid: uint32(os.Geteuid()),
				Gid: uint32(os.Getegid()),
			},
		},
	})
	if err != nil {
		fmt.Printf("os/exec returned an error: '%s'\n", err)
		os.Exit(1)
	}

	// wait for daemon to be ready
	_, err = proc.Wait()
	if err != nil {
		fmt.Printf("proc.Wait() failed. %s\n", err)
		os.Exit(1)
	}
}

func stopDaemon() {
	pidFile, err := os.Open(flags.PIDFile)
	if err != nil {
		if err == os.ErrNotExist {
			fmt.Printf("it looks like npserver is not running")
			os.Exit(0)
		}
		fmt.Printf("error on opening pidfile: %s\n", err)
		os.Exit(1)
	}

	pidFileContents, err := ioutil.ReadAll(pidFile)
	pidFile.Close()
	if err != nil {
		fmt.Printf("error reading pidfile contents: %s\n", err)
		os.Exit(1)
	}

	// convert pid string to pid int
	pid, err := strconv.Atoi(string(pidFileContents))
	if err != nil {
		fmt.Printf("error parsing pidfile contents: %s\n", err)
		os.Exit(1)
	}

	// lookup process
	proc, err := os.FindProcess(pid)
	if err != nil {
		fmt.Printf("error finding process with pid %d: %s\n", pid, err)
		os.Exit(1)
	}

	// signal process to stop
	err = proc.Signal(os.Interrupt)
	if err != nil {
		fmt.Printf("error sending interrupt signal to npserver: %s\n", err)
		os.Exit(1)
	}

	// wait until process is done
	state, err := proc.Wait()
	if err != nil {
		fmt.Printf("error waiting for process to stop: %s\n", err)
		os.Exit(1)
	}
	if !state.Exited() || !state.Success() {
		fmt.Printf("npserver process exited badly")
		os.Exit(1)
	}

	// remove pid file
	err := os.Remove(flags.PIDFile)
	if err != nil {
		fmt.Printf("error removing pid file: %s\n", err)
	}
}
<commit_msg>bugfix<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"syscall"
)

func main() {

	initFlags()

	// check permissions
	if os.Getuid() != 0 && os.Geteuid() != 0 {
		fmt.Printf("npserver-daemon should be run as root, have uid=%d and euid=%d\n", os.Getuid(), os.Geteuid())
		os.Exit(1)
	}

	// check start || sto
	if flags.Start == flags.Stop {
		fmt.Println("need --start or --stop flag")
	}

	if flags.Start {
		startDaemon()
	}
	if flags.Stop {
		stopDaemon()
	}

	// all good :)
}

func startDaemon() {
	// setup args for daemon call
	args := []string{
		"--name=npserver",
		"--noconfig",
		"--errlog=/var/log/npserver-daemon.log",
		"--output=/var/log/npserver.log",
		fmt.Sprintf("--pidfile=%s", flags.PIDFile),
		"--unsafe",
		"--",
		"/usr/local/bin/npserver",
	}

	// append extra args to args
	args = append(args, extraArgs...)

	// start process
	proc, err := os.StartProcess("daemon", args, &os.ProcAttr{
		Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
		Sys: &syscall.SysProcAttr{
			Credential: &syscall.Credential{
				Uid: uint32(os.Geteuid()),
				Gid: uint32(os.Getegid()),
			},
		},
	})
	if err != nil {
		fmt.Printf("os/exec returned an error: '%s'\n", err)
		os.Exit(1)
	}

	// wait for daemon to be ready
	_, err = proc.Wait()
	if err != nil {
		fmt.Printf("proc.Wait() failed. %s\n", err)
		os.Exit(1)
	}
}

func stopDaemon() {
	pidFile, err := os.Open(flags.PIDFile)
	if err != nil {
		if err == os.ErrNotExist {
			fmt.Printf("it looks like npserver is not running")
			os.Exit(0)
		}
		fmt.Printf("error on opening pidfile: %s\n", err)
		os.Exit(1)
	}

	pidFileContents, err := ioutil.ReadAll(pidFile)
	pidFile.Close()
	if err != nil {
		fmt.Printf("error reading pidfile contents: %s\n", err)
		os.Exit(1)
	}

	// convert pid string to pid int
	pid, err := strconv.Atoi(string(pidFileContents))
	if err != nil {
		fmt.Printf("error parsing pidfile contents: %s\n", err)
		os.Exit(1)
	}

	// lookup process
	proc, err := os.FindProcess(pid)
	if err != nil {
		fmt.Printf("error finding process with pid %d: %s\n", pid, err)
		os.Exit(1)
	}

	// signal process to stop
	err = proc.Signal(os.Interrupt)
	if err != nil {
		fmt.Printf("error sending interrupt signal to npserver: %s\n", err)
		os.Exit(1)
	}

	// wait until process is done
	state, err := proc.Wait()
	if err != nil {
		fmt.Printf("error waiting for process to stop: %s\n", err)
		os.Exit(1)
	}
	if !state.Exited() || !state.Success() {
		fmt.Printf("npserver process exited badly")
		os.Exit(1)
	}

	// remove pid file
	err = os.Remove(flags.PIDFile)
	if err != nil {
		fmt.Printf("error removing pid file: %s\n", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/user"
	"strings"
)

// Size of internal buffer for reading. 4kB should be enough to cover even the
// poorest implementation's idea of a longest permissible line.
const maxlinesize = 4096

// Maximum length of a line we output - anything else will be wrapped
const lineLength = 80

// Size of the ServerMessage buffer
const serverMsgBufSize = 10

// A message sent by the server
type ServerMessage struct {
	From string
	Code string
	To   string

	// The raw content of the message, excluding From, Code, and To
	Raw string

	// The full faw content of the message
	Full string

	// All other fields.
	Fields []string
}

type Client struct {
	connection net.Conn
	serverName string
	nick       string
	server     chan ServerMessage
	out        io.Writer
}

func nextField(line string) (string, string, bool) {
	fs := strings.SplitN(line, " ", 2)
	if len(fs) > 1 {
		return fs[0], fs[1], true
	}
	return fs[0], "", false
}

func parseServerMessage(line string) (m ServerMessage) {
	m.Full = line
	m.From, line, _ = nextField(line)
	m.From = m.From[1:]
	m.Code, line, _ = nextField(line)
	m.To, line, _ = nextField(line)
	m.Raw = line
	m.Fields = make([]string, 0)

	switch m.Code {
	case "NOTICE":
	case "PRIVMSG":
		m.Fields = append(m.Fields, line[1:])
	case RPL_BOUNCE:
	default:
		// fill in variable fields
		var f string
		f, line, b := nextField(line)
		for b {
			if f[0] == ':' {
				if f[1] == '-' {
					break
				}
				// read until f[len(f)-1] is a semicolon
				for b && len(f) > 0 && f[len(f)-1] != ':' {
					f, line, b = nextField(line)
				}
			} else {
				m.Fields = append(m.Fields, f)
			}
			f, line, b = nextField(line)
		}
	}
	return
}

func getUname() string {
	uid := os.Getuid()
	uname := "go"
	u, e := user.LookupId(uid)
	if e != nil {
		log.Print("WARNING: user.Lookupid: ", e)
	} else {
		uname = u.Username
	}
	return uname
}

func getHostname() string {
	n, e := os.Hostname()
	if e != nil {
		log.Print("WARNING: os.Hostname: ", e)
		n = "*"
	}
	return n
}

func getServername() string {
	return "*"
}

func Connect(serverName, nick, realName string) (Client, error) {
	conn, err := Dial(serverName)
	if err != nil {
		return conn, err
	}

	conn.Pass("notmeaningfull")
	conn.User(getUname(), getHostname(), getServername(), realName)
	conn.Nick(nick)
	return conn, nil
}

// Low-level method to connect to server - normal clients should not need this.
// Use Connect() instead.
func Dial(server string) (conn Client, err error) {
	nconn, err := net.Dial("tcp", server)
	if err != nil {
		return
	}

	conn.connection = nconn
	conn.serverName = server
	conn.server = make(chan ServerMessage, serverMsgBufSize)
	conn.out = conn.connection

	// spawn the connection reader
	go func() {
		r, err := bufio.NewReaderSize(conn.connection, maxlinesize)
		if err != nil {
			panic(err)
		}

		line, beFalse, err := r.ReadLine()
		for err == nil && !beFalse {
			conn.server <- parseServerMessage(string(line))
			line, beFalse, err = r.ReadLine()
		}
		if beFalse {
			log.Fatal("Line too long")
		} else {
			log.Fatal(err) // TODO handle me better
		}
	}()
	return
}

func (c Client) Pass(pass string) {
	fmt.Fprintf(c.out, "PASS %s\n", pass)
}

// Low-level method to send USER command - normal clients should not need this
func (c Client) User(user, host, server, name string) {
	fmt.Fprintf(c.out, "USER %s %s %s :%s\n", user, host, server, name)
}

// Low-level method to send NICK command - normal clients should not need this.
func (c Client) Nick(nick string) {
	fmt.Fprintf(c.out, "NICK %s\n", nick)
	c.nick = nick // TODO fix possible race condition
}

// Listen for messages coming in and return them on the returned channel. Also
// handles low-level information from the server correctly, making information
// available in the Client object as appropriate.
func (c Client) Listen() (<-chan Message) {
	ch := make(chan Message)
	handleMessage := func(sm ServerMessage) {
		switch sm.Code {
		case "NOTICE":
			ch <- Message{
				Kind: MSG_NOTICE,
				From: sm.From,
				To: sm.To,
				Text: sm.Raw[1:],
			}
		case "PRIVMSG":
			ch <- Message{
				Kind: MSG_PRIVMSG,
				From: sm.From,
				To: sm.To,
				Text: sm.Raw[1:],
			}
		case RPL_MOTD:
		case RPL_MOTDSTART:
		case RPL_ENDOFMOTD:
		case RPL_WELCOME:
		case RPL_YOURHOST:
		case RPL_CREATED:
		case RPL_MYINFO:
		case RPL_BOUNCE:
		case RPL_LUSERCLIENT:
		case RPL_LUSEROP:
		case RPL_LUSERUNKNOWN:
		case RPL_LUSERCHANNELS:
		case RPL_LUSERME:
		case "MODE":
		default:
			log.Printf("Unhandled message %s\t\t%s", sm.Code, sm.Full)
		}
	}
	go func() {
		for {
			var m ServerMessage
			select {
			// TODO allow input on stdin
			case m = <-c.server:
				handleMessage(m)
			}
		}
	}()
	return ch
}

// Join the specified channel
func (c Client) Join(ch string) {
	// TODO don't join until 001 is received
	log.Print("JOIN ", ch)
	fmt.Fprintf(c.out, "JOIN %s\n", ch)
}

// Join the specified channels
func (c Client) JoinChannels(chs []string) {
	for _, ch := range chs {
		c.Join(ch)
	}
}

// Leave the specified channel
func (c Client) Part(ch string) {
	log.Print("PART ", ch)
	fmt.Fprintf(c.out, "PART %s\n", ch)
}

func (c Client) Quit(msg string) {
	log.Print("QUIT :", msg)
	fmt.Fprintf(c.out, "QUIT :%s\n", msg)
}

func (c Client) PrivMsg(to, msg string) {
	fmt.Fprintf(c.out, "PRIVMSG %s :%s\n", to, msg)
}
<commit_msg>Handling PINGs appropriately<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/user"
	"strings"
)

// Size of internal buffer for reading. 4kB should be enough to cover even the
// poorest implementation's idea of a longest permissible line.
const maxlinesize = 4096

// Maximum length of a line we output - anything else will be wrapped
const lineLength = 80

// Size of the ServerMessage buffer
const serverMsgBufSize = 10

// A message sent by the server
type ServerMessage struct {
	From string
	Code string
	To   string

	// The raw content of the message, excluding From, Code, and To
	Raw string

	// The full faw content of the message
	Full string

	// All other fields.
	Fields []string
}

type Client struct {
	connection net.Conn
	serverName string
	nick       string
	server     chan ServerMessage
	out        io.Writer
}

func nextField(line string) (string, string, bool) {
	fs := strings.SplitN(line, " ", 2)
	if len(fs) > 1 {
		return fs[0], fs[1], true
	}
	return fs[0], "", false
}

func parseServerMessage(line string) (m ServerMessage) {
	m.Full = line
	m.From, line, _ = nextField(line)
	if m.From[0] != ':' {
		m.Code = m.From
		m.From = ""
	} else {
		m.From = m.From[1:]
		m.Code, line, _ = nextField(line)
	}
	m.To, line, _ = nextField(line)
	m.Raw = line
	m.Fields = make([]string, 0)

	switch m.Code {
	case "NOTICE":
	case "PRIVMSG":
		m.Fields = append(m.Fields, line[1:])
	case RPL_BOUNCE:
	default:
		// fill in variable fields
		var f string
		f, line, b := nextField(line)
		for b {
			if f[0] == ':' {
				if f[1] == '-' {
					break
				}
				// read until f[len(f)-1] is a semicolon
				for b && len(f) > 0 && f[len(f)-1] != ':' {
					f, line, b = nextField(line)
				}
			} else {
				m.Fields = append(m.Fields, f)
			}
			f, line, b = nextField(line)
		}
	}
	return
}

func getUname() string {
	uid := os.Getuid()
	uname := "go"
	u, e := user.LookupId(uid)
	if e != nil {
		log.Print("WARNING: user.Lookupid: ", e)
	} else {
		uname = u.Username
	}
	return uname
}

func getHostname() string {
	n, e := os.Hostname()
	if e != nil {
		log.Print("WARNING: os.Hostname: ", e)
		n = "*"
	}
	return n
}

func getServername() string {
	return "*"
}

func Connect(serverName, nick, realName string) (Client, error) {
	conn, err := Dial(serverName)
	if err != nil {
		return conn, err
	}

	conn.Pass("notmeaningfull")
	conn.User(getUname(), getHostname(), getServername(), realName)
	conn.Nick(nick)
	return conn, nil
}

// Low-level method to connect to server - normal clients should not need this.
// Use Connect() instead.
func Dial(server string) (conn Client, err error) {
	nconn, err := net.Dial("tcp", server)
	if err != nil {
		return
	}

	conn.connection = nconn
	conn.serverName = server
	conn.server = make(chan ServerMessage, serverMsgBufSize)
	conn.out = conn.connection

	// spawn the connection reader
	go func() {
		r, err := bufio.NewReaderSize(conn.connection, maxlinesize)
		if err != nil {
			panic(err)
		}

		line, beFalse, err := r.ReadLine()
		for err == nil && !beFalse {
			conn.server <- parseServerMessage(string(line))
			line, beFalse, err = r.ReadLine()
		}
		if beFalse {
			log.Fatal("Line too long")
		} else {
			log.Fatal(err) // TODO handle me better
		}
	}()
	return
}

func (c Client) Pass(pass string) {
	fmt.Fprintf(c.out, "PASS %s\n", pass)
}

// Low-level method to send USER command - normal clients should not need this
func (c Client) User(user, host, server, name string) {
	fmt.Fprintf(c.out, "USER %s %s %s :%s\n", user, host, server, name)
}

// Low-level method to send NICK command - normal clients should not need this.
func (c Client) Nick(nick string) {
	fmt.Fprintf(c.out, "NICK %s\n", nick)
	c.nick = nick // TODO fix possible race condition
}

// Listen for messages coming in and return them on the returned channel. Also
// handles low-level information from the server correctly, making information
// available in the Client object as appropriate.
func (c Client) Listen() (<-chan Message) {
	ch := make(chan Message)
	handleMessage := func(sm ServerMessage) {
		switch sm.Code {
		case "NOTICE":
			ch <- Message{
				Kind: MSG_NOTICE,
				From: sm.From,
				To: sm.To,
				Text: sm.Raw[1:],
			}
		case "PRIVMSG":
			ch <- Message{
				Kind: MSG_PRIVMSG,
				From: sm.From,
				To: sm.To,
				Text: sm.Raw[1:],
			}
		case "PING":
			fmt.Fprintf(c.out, "PONG :%s\n", sm.To)
		case RPL_MOTD:
		case RPL_MOTDSTART:
		case RPL_ENDOFMOTD:
		case RPL_WELCOME:
		case RPL_YOURHOST:
		case RPL_CREATED:
		case RPL_MYINFO:
		case RPL_BOUNCE:
		case RPL_LUSERCLIENT:
		case RPL_LUSEROP:
		case RPL_LUSERUNKNOWN:
		case RPL_LUSERCHANNELS:
		case RPL_LUSERME:
		case "MODE":
		default:
			log.Printf("Unhandled message %s\t\t%s", sm.Code, sm.Full)
		}
	}
	go func() {
		for {
			var m ServerMessage
			select {
			// TODO allow input on stdin
			case m = <-c.server:
				handleMessage(m)
			}
		}
	}()
	return ch
}

// Join the specified channel
func (c Client) Join(ch string) {
	// TODO don't join until 001 is received
	log.Print("JOIN ", ch)
	fmt.Fprintf(c.out, "JOIN %s\n", ch)
}

// Join the specified channels
func (c Client) JoinChannels(chs []string) {
	for _, ch := range chs {
		c.Join(ch)
	}
}

// Leave the specified channel
func (c Client) Part(ch string) {
	log.Print("PART ", ch)
	fmt.Fprintf(c.out, "PART %s\n", ch)
}

func (c Client) Quit(msg string) {
	log.Print("QUIT :", msg)
	fmt.Fprintf(c.out, "QUIT :%s\n", msg)
}

func (c Client) PrivMsg(to, msg string) {
	fmt.Fprintf(c.out, "PRIVMSG %s :%s\n", to, msg)
}
<|endoftext|>


<commit_before>package twitch

import (
	"fmt"
	"regexp"
	"strings"
)

type ircMessage struct {
	Raw     string
	Tags    map[string]string
	Source  ircMessageSource
	Command string
	Params  []string
}

type ircMessageSource struct {
	Nickname string
	Username string
	Host     string
}

func parseIRCMessage(line string) (*ircMessage, error) {
	message := ircMessage{
		Raw:    line,
		Tags:   make(map[string]string),
		Params: []string{},
	}

	split := strings.Split(line, " ")
	index := 0

	if strings.HasPrefix(split[index], "@") {
		message.Tags = parseIRCTags(split[index])
		index++
	}

	if index >= len(split) {
		return &message, fmt.Errorf("parseIRCMessage: partial message")
	}

	if strings.HasPrefix(split[index], ":") {
		message.Source = *parseIRCMessageSource(split[index])
		index++
	}

	if index >= len(split) {
		return &message, fmt.Errorf("parseIRCMessage: no command")
	}

	message.Command = split[index]
	index++

	if index >= len(split) {
		return &message, nil
	}

	var params []string
	for i, v := range split[index:] {
		if strings.HasPrefix(v, ":") {
			v = strings.Join(split[index+i:], " ")
			v = strings.TrimPrefix(v, ":")
			params = append(params, v)
			break
		}

		params = append(params, v)
	}

	message.Params = params

	return &message, nil
}

func parseIRCTags(rawTags string) map[string]string {
	tags := make(map[string]string)

	rawTags = strings.TrimPrefix(rawTags, "@")

	for _, tag := range strings.Split(rawTags, ";") {
		if !strings.Contains(tag, "=") {
			continue
		}

		pair := strings.SplitN(tag, "=", 2)
		key := pair[0]

		var value string
		if len(pair) == 2 {
			value = parseIRCTagValue(pair[1])
		}

		tags[key] = value
	}

	return tags
}

var escapeCharacters = map[string]string{
	"\\s":  " ",
	"\\n":  "",
	"\\:":  ";",
	"\\\\": "\\",
}

func parseIRCTagValue(rawValue string) string {
	for char, value := range escapeCharacters {
		rawValue = strings.Replace(rawValue, char, value, -1)
	}

	rawValue = strings.TrimSuffix(rawValue, "\\")
	rawValue = strings.TrimSpace(rawValue)

	return rawValue
}

func parseIRCMessageSource(rawSource string) *ircMessageSource {
	var source ircMessageSource

	rawSource = strings.TrimPrefix(rawSource, ":")

	regex := regexp.MustCompile(`!|@`)
	split := regex.Split(rawSource, -1)

	if len(split) == 1 {
		source.Host = split[0]
	} else {
		source.Nickname = split[0]
		source.Username = split[1]
		source.Host = split[2]
	}

	return &source
}
<commit_msg>Minor IRC parsing fixes<commit_after>package twitch

import (
	"fmt"
	"regexp"
	"strings"
)

type ircMessage struct {
	Raw     string
	Tags    map[string]string
	Source  ircMessageSource
	Command string
	Params  []string
}

type ircMessageSource struct {
	Nickname string
	Username string
	Host     string
}

func parseIRCMessage(line string) (*ircMessage, error) {
	message := ircMessage{
		Raw:    line,
		Tags:   make(map[string]string),
		Params: []string{},
	}

	split := strings.Split(line, " ")
	index := 0

	if strings.HasPrefix(split[index], "@") {
		message.Tags = parseIRCTags(split[index])
		index++
	}

	if index >= len(split) {
		return &message, fmt.Errorf("parseIRCMessage: partial message")
	}

	if strings.HasPrefix(split[index], ":") {
		message.Source = *parseIRCMessageSource(split[index])
		index++
	}

	if index >= len(split) {
		return &message, fmt.Errorf("parseIRCMessage: no command")
	}

	message.Command = split[index]
	index++

	if index >= len(split) {
		return &message, nil
	}

	var params []string
	for i, v := range split[index:] {
		if strings.HasPrefix(v, ":") {
			v = strings.Join(split[index+i:], " ")
			v = strings.TrimPrefix(v, ":")
			params = append(params, v)
			break
		}

		params = append(params, v)
	}

	message.Params = params

	return &message, nil
}

func parseIRCTags(rawTags string) map[string]string {
	tags := make(map[string]string)

	rawTags = strings.TrimPrefix(rawTags, "@")

	for _, tag := range strings.Split(rawTags, ";") {
		pair := strings.SplitN(tag, "=", 2)
		key := pair[0]

		var value string
		if len(pair) == 2 {
			value = parseIRCTagValue(pair[1])
		}

		tags[key] = value
	}

	return tags
}

var escapeCharacters = map[string]string{
	"\\s":  " ",
	"\\n":  "",
	"\\r":  "",
	"\\:":  ";",
	"\\\\": "\\",
}

func parseIRCTagValue(rawValue string) string {
	for char, value := range escapeCharacters {
		rawValue = strings.Replace(rawValue, char, value, -1)
	}

	rawValue = strings.TrimSuffix(rawValue, "\\")

	// Some Twitch values can end with a trailing \s
	// Example: "system-msg=An\sanonymous\suser\sgifted\sa\sTier\s1\ssub\sto\sTenureCalculator!\s"
	rawValue = strings.TrimSpace(rawValue)

	return rawValue
}

func parseIRCMessageSource(rawSource string) *ircMessageSource {
	var source ircMessageSource

	rawSource = strings.TrimPrefix(rawSource, ":")

	regex := regexp.MustCompile(`!|@`)
	split := regex.Split(rawSource, -1)

	if len(split) == 1 {
		source.Host = split[0]
	} else {
		source.Nickname = split[0]
		source.Username = split[1]
		source.Host = split[2]
	}

	return &source
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/golang/glog"
	_ "github.com/golang/glog"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"github.com/open-policy-agent/frameworks/constraint/pkg/compiler/regorewriter"
)

var rootCmd = &cobra.Command{
	Use:   "rewrite-compatibility",
	Short: "rewrite-compatibility is the constraint framework package path rewriter compatibility tool",
	Long: `
The rewrite-compatibility exists to dump out the rego source as it would exist after a rewrite in
the constraint framework.  The idea is to allow user visibility into what's going on as well as
facilitate developer debugging when making changes to the rewriter.

Example usage for transforming the forseti-security/policy-library constraints:
git clone git@github.com:forseti-security/policy-library.git
go run ./cmd/rewrite-compatibility/main.go \
  --ct policy-library/validator \
  --lib policy-library/lib \
  --input ./policy-library \
  --output ./policy-library-rewrite \
  --pkgPrefix x.y.z \
  --alsologtostderr
opa test -v rewrite/lib/ rewrite/validator/
meld policy-library/lib/ rewrite/lib/
meld policy-library/validator/ rewrite/validator/
`,
	RunE: rootCmdFn,
}

var (
	cts       []string
	libs      []string
	pkgPrefix string
	oldRoot   string
	newRoot   string
)

func init() {
	rootCmd.Flags().StringSliceVar(
		&cts, "ct", nil, "The rego for the constraint template body")
	rootCmd.Flags().StringSliceVar(
		&libs, "lib", nil, "Libs associated with the rego, can be file or directory")
	rootCmd.Flags().StringVar(
		&pkgPrefix, "pkgPrefix", "", "The new prefix to insert into the package path")
	rootCmd.Flags().StringVarP(
		&oldRoot, "input", "i", "", "The input 'root' directory, outputs will be")
	rootCmd.Flags().StringVarP(
		&newRoot, "output", "o", "", "The output 'root' directory, outputs will be")
	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
}

func compileSrcs(
	cts []string,
	libs []string,
	newPkgPrefix string,
	oldRoot string,
	newRoot string) error {
	if len(cts) == 0 && len(libs) == 0 {
		return errors.Errorf("must specify --ct or --lib or both")
	}
	if (oldRoot == "") != (newRoot == "") {
		return errors.Errorf("--input and --output must be empty or non empty together")
	}

	regoRewriter, err := regorewriter.New(
		regorewriter.NewPackagePrefixer(newPkgPrefix),
		[]string{
			"data.lib",
		},
		[]string{
			"data.inventory",
		},
	)

	if err != nil {
		return err
	}

	for _, ct := range cts {
		if err := regoRewriter.AddBaseFromFs(ct); err != nil {
			return err
		}
	}

	for _, libPath := range libs {
		if err := regoRewriter.AddLibFromFs(libPath); err != nil {
			return err
		}
	}

	srcs, err := regoRewriter.Rewrite()
	if err != nil {
		return err
	}

	if oldRoot != "" && newRoot != "" {
		if err := srcs.Reparent(oldRoot, newRoot); err != nil {
			return err
		}
		if err := srcs.Write(); err != nil {
			return err
		}
	}

	glog.Info("SUCCESS!")
	return nil
}

func rootCmdFn(cmd *cobra.Command, args []string) error {
	return compileSrcs(cts, libs, pkgPrefix, oldRoot, newRoot)
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Printf("%+v\n", err)
		os.Exit(1)
	}
}
<commit_msg>Fix import path.<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/golang/glog"
	_ "github.com/golang/glog"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"github.com/open-policy-agent/frameworks/constraint/pkg/regorewriter"
)

var rootCmd = &cobra.Command{
	Use:   "rewrite-compatibility",
	Short: "rewrite-compatibility is the constraint framework package path rewriter compatibility tool",
	Long: `
The rewrite-compatibility exists to dump out the rego source as it would exist after a rewrite in
the constraint framework.  The idea is to allow user visibility into what's going on as well as
facilitate developer debugging when making changes to the rewriter.

Example usage for transforming the forseti-security/policy-library constraints:
git clone git@github.com:forseti-security/policy-library.git
go run ./cmd/rewrite-compatibility/main.go \
  --ct policy-library/validator \
  --lib policy-library/lib \
  --input ./policy-library \
  --output ./policy-library-rewrite \
  --pkgPrefix x.y.z \
  --alsologtostderr
opa test -v rewrite/lib/ rewrite/validator/
meld policy-library/lib/ rewrite/lib/
meld policy-library/validator/ rewrite/validator/
`,
	RunE: rootCmdFn,
}

var (
	cts       []string
	libs      []string
	pkgPrefix string
	oldRoot   string
	newRoot   string
)

func init() {
	rootCmd.Flags().StringSliceVar(
		&cts, "ct", nil, "The rego for the constraint template body")
	rootCmd.Flags().StringSliceVar(
		&libs, "lib", nil, "Libs associated with the rego, can be file or directory")
	rootCmd.Flags().StringVar(
		&pkgPrefix, "pkgPrefix", "", "The new prefix to insert into the package path")
	rootCmd.Flags().StringVarP(
		&oldRoot, "input", "i", "", "The input 'root' directory, outputs will be")
	rootCmd.Flags().StringVarP(
		&newRoot, "output", "o", "", "The output 'root' directory, outputs will be")
	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
}

func compileSrcs(
	cts []string,
	libs []string,
	newPkgPrefix string,
	oldRoot string,
	newRoot string) error {
	if len(cts) == 0 && len(libs) == 0 {
		return errors.Errorf("must specify --ct or --lib or both")
	}
	if (oldRoot == "") != (newRoot == "") {
		return errors.Errorf("--input and --output must be empty or non empty together")
	}

	regoRewriter, err := regorewriter.New(
		regorewriter.NewPackagePrefixer(newPkgPrefix),
		[]string{
			"data.lib",
		},
		[]string{
			"data.inventory",
		},
	)

	if err != nil {
		return err
	}

	for _, ct := range cts {
		if err := regoRewriter.AddBaseFromFs(ct); err != nil {
			return err
		}
	}

	for _, libPath := range libs {
		if err := regoRewriter.AddLibFromFs(libPath); err != nil {
			return err
		}
	}

	srcs, err := regoRewriter.Rewrite()
	if err != nil {
		return err
	}

	if oldRoot != "" && newRoot != "" {
		if err := srcs.Reparent(oldRoot, newRoot); err != nil {
			return err
		}
		if err := srcs.Write(); err != nil {
			return err
		}
	}

	glog.Info("SUCCESS!")
	return nil
}

func rootCmdFn(cmd *cobra.Command, args []string) error {
	return compileSrcs(cts, libs, pkgPrefix, oldRoot, newRoot)
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Printf("%+v\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


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

package doc

import (
	"errors"
	"net/http"
	"regexp"
	"strconv"
	"strings"

	"github.com/Unknwon/gowalker/utils"
)

var (
	googleRepoRe     = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)
	googleRevisionRe = regexp.MustCompile(`<h2>(?:[^ ]+ - )?Revision *([^:]+):`)
	googleEtagRe     = regexp.MustCompile(`^(hg|git|svn)-`)
	googleFileRe     = regexp.MustCompile(`<li><a href="([^"/]+)"`)
	googleDirRe      = regexp.MustCompile(`<li><a href="([^".]+)"`)
	googlePattern    = regexp.MustCompile(`^code\.google\.com/p/(?P<repo>[a-z0-9\-]+)(:?\.(?P<subrepo>[a-z0-9\-]+))?(?P<dir>/[a-z0-9A-Z_.\-/]+)?$`)
)

func getStandardDoc(client *http.Client, importPath string, savedEtag string) (pdoc *Package, err error) {
	p, err := httpGetBytes(client, "http://go.googlecode.com/hg-history/release/src/pkg/"+importPath+"/", nil)
	if err != nil {
		return nil, err
	}

	// Check revision tag.
	var etag string
	if m := googleRevisionRe.FindSubmatch(p); m == nil {
		return nil, errors.New("doc.getStandardDoc(): Could not find revision for " + importPath)
	} else {
		etag = string(m[1])
		if etag == savedEtag {
			return nil, errNotModified
		}
	}

	// Get source file data.
	files := make([]*source, 0, 5)
	for _, m := range googleFileRe.FindAllSubmatch(p, -1) {
		fname := strings.Split(string(m[1]), "?")[0]
		if utils.IsDocFile(fname) {
			files = append(files, &source{
				name:      fname,
				browseURL: "http://code.google.com/p/go/source/browse/src/pkg/" + importPath + "/" + fname + "?name=release",
				rawURL:    "http://go.googlecode.com/hg-history/release/src/pkg/" + importPath + "/" + fname,
			})
		}
	}

	dirs := make([]string, 0, 5)
	// Get subdirectories.
	for _, m := range googleDirRe.FindAllSubmatch(p, -1) {
		dirName := strings.Split(string(m[1]), "?")[0]
		// Make sure we get directories.
		if strings.HasSuffix(dirName, "/") {
			dirs = append(dirs, strings.Replace(dirName, "/", "", -1))
		}
	}

	if len(files) == 0 && len(dirs) == 0 {
		return nil, NotFoundError{"Directory tree does not contain Go files and subdirs."}
	}

	// Fetch file from VCS.
	if err := fetchFiles(client, files, nil); err != nil {
		return nil, err
	}

	// Start generating data.
	w := &walker{
		lineFmt: "#%d",
		pdoc: &Package{
			ImportPath:  importPath,
			ProjectName: "Go",
			Etag:        etag,
			Dirs:        dirs,
		},
	}
	return w.build(files)
}

func getGoogleDoc(client *http.Client, match map[string]string, savedEtag string) (*Package, error) {
	setupGoogleMatch(match)
	if m := googleEtagRe.FindStringSubmatch(savedEtag); m != nil {
		match["vcs"] = m[1]
	} else if err := getGoogleVCS(client, match); err != nil {
		return nil, err
	}

	// Scrape the repo browser to find the project revision and individual Go files.
	p, err := httpGetBytes(client, expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/", match), nil)
	if err != nil {
		return nil, err
	}

	// Check revision tag.
	var etag string
	if m := googleRevisionRe.FindSubmatch(p); m == nil {
		return nil, errors.New("doc.getGoogleDoc(): Could not find revision for " + match["importPath"])
	} else {
		etag = expand("{vcs}-{0}", match, string(m[1]))
		if etag == savedEtag {
			return nil, errNotModified
		}
	}

	// Get source file data.
	files := make([]*source, 0, 5)
	for _, m := range googleFileRe.FindAllSubmatch(p, -1) {
		fname := string(m[1])
		if utils.IsDocFile(fname) {
			files = append(files, &source{
				name:      fname,
				browseURL: expand("http://code.google.com/p/{repo}/source/browse{dir}/{0}{query}", match, fname),
				rawURL:    expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/{0}", match, fname),
			})
		}
	}

	if len(files) == 0 {
		return nil, NotFoundError{"Directory tree does not contain Go files."}
	}

	dirs := make([]string, 0, 3)
	// Get subdirectories.
	for _, m := range googleDirRe.FindAllSubmatch(p, -1) {
		dirName := strings.Split(string(m[1]), "?")[0]
		dirs = append(dirs, strings.Replace(dirName, "/", "", -1))
	}

	// Fetch file from VCS.
	if err := fetchFiles(client, files, nil); err != nil {
		return nil, err
	}

	// Start generating data.
	w := &walker{
		lineFmt: "#%d",
		pdoc: &Package{
			ImportPath:  match["importPath"],
			ProjectName: expand("{repo}{dot}{subrepo}", match),
			Etag:        etag,
			Dirs:        dirs,
		},
	}
	return w.build(files)
}

func setupGoogleMatch(match map[string]string) {
	if s := match["subrepo"]; s != "" {
		match["dot"] = "."
		match["query"] = "?repo=" + s
	} else {
		match["dot"] = ""
		match["query"] = ""
	}
}

func getGoogleVCS(client *http.Client, match map[string]string) error {
	// Scrape the HTML project page to find the VCS.
	p, err := httpGetBytes(client, expand("http://code.google.com/p/{repo}/source/checkout", match), nil)
	if err != nil {
		return err
	}
	m := googleRepoRe.FindSubmatch(p)
	if m == nil {
		return NotFoundError{"Could not VCS on Google Code project page."}
	}
	match["vcs"] = string(m[1])
	return nil
}

// expand replaces {k} in template with match[k] or subs[atoi(k)] if k is not in match.
func expand(template string, match map[string]string, subs ...string) string {
	var p []byte
	var i int
	for {
		i = strings.Index(template, "{")
		if i < 0 {
			break
		}
		p = append(p, template[:i]...)
		template = template[i+1:]
		i = strings.Index(template, "}")
		if s, ok := match[template[:i]]; ok {
			p = append(p, s...)
		} else {
			j, _ := strconv.Atoi(template[:i])
			p = append(p, subs[j]...)
		}
		template = template[i+1:]
	}
	p = append(p, template...)
	return string(p)
}
<commit_msg>bug fix<commit_after>// Copyright 2011 Gary Burd
// Copyright 2013 Unknown
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package doc

import (
	"errors"
	"net/http"
	"regexp"
	"strconv"
	"strings"

	"github.com/Unknwon/gowalker/utils"
)

var (
	googleRepoRe     = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)
	googleRevisionRe = regexp.MustCompile(`<h2>(?:[^ ]+ - )?Revision *([^:]+):`)
	googleEtagRe     = regexp.MustCompile(`^(hg|git|svn)-`)
	googleFileRe     = regexp.MustCompile(`<li><a href="([^"/]+)"`)
	googleDirRe      = regexp.MustCompile(`<li><a href="([^".]+)"`)
	googlePattern    = regexp.MustCompile(`^code\.google\.com/p/(?P<repo>[a-z0-9\-]+)(:?\.(?P<subrepo>[a-z0-9\-]+))?(?P<dir>/[a-z0-9A-Z_.\-/]+)?$`)
)

func getStandardDoc(client *http.Client, importPath string, savedEtag string) (pdoc *Package, err error) {
	p, err := httpGetBytes(client, "http://go.googlecode.com/hg-history/release/src/pkg/"+importPath+"/", nil)
	if err != nil {
		return nil, err
	}

	// Check revision tag.
	var etag string
	if m := googleRevisionRe.FindSubmatch(p); m == nil {
		return nil, errors.New("doc.getStandardDoc(): Could not find revision for " + importPath)
	} else {
		etag = string(m[1])
		if etag == savedEtag {
			return nil, errNotModified
		}
	}

	// Get source file data.
	files := make([]*source, 0, 5)
	for _, m := range googleFileRe.FindAllSubmatch(p, -1) {
		fname := strings.Split(string(m[1]), "?")[0]
		if utils.IsDocFile(fname) {
			files = append(files, &source{
				name:      fname,
				browseURL: "http://code.google.com/p/go/source/browse/src/pkg/" + importPath + "/" + fname + "?name=release",
				rawURL:    "http://go.googlecode.com/hg-history/release/src/pkg/" + importPath + "/" + fname,
			})
		}
	}

	dirs := make([]string, 0, 5)
	// Get subdirectories.
	for _, m := range googleDirRe.FindAllSubmatch(p, -1) {
		dirName := strings.Split(string(m[1]), "?")[0]
		// Make sure we get directories.
		if strings.HasSuffix(dirName, "/") {
			dirs = append(dirs, strings.Replace(dirName, "/", "", -1))
		}
	}

	if len(files) == 0 && len(dirs) == 0 {
		return nil, NotFoundError{"Directory tree does not contain Go files and subdirs."}
	}

	// Fetch file from VCS.
	if err := fetchFiles(client, files, nil); err != nil {
		return nil, err
	}

	// Start generating data.
	w := &walker{
		lineFmt: "#%d",
		pdoc: &Package{
			ImportPath:  importPath,
			ProjectName: "Go",
			Etag:        etag,
			Dirs:        dirs,
		},
	}
	return w.build(files)
}

func getGoogleDoc(client *http.Client, match map[string]string, savedEtag string) (*Package, error) {
	setupGoogleMatch(match)
	if m := googleEtagRe.FindStringSubmatch(savedEtag); m != nil {
		match["vcs"] = m[1]
	} else if err := getGoogleVCS(client, match); err != nil {
		return nil, err
	}

	// Scrape the repo browser to find the project revision and individual Go files.
	p, err := httpGetBytes(client, expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/", match), nil)
	if err != nil {
		return nil, err
	}

	// Check revision tag.
	var etag string
	if m := googleRevisionRe.FindSubmatch(p); m == nil {
		return nil, errors.New("doc.getGoogleDoc(): Could not find revision for " + match["importPath"])
	} else {
		etag = expand("{vcs}-{0}", match, string(m[1]))
		if etag == savedEtag {
			return nil, errNotModified
		}
	}

	// Get source file data.
	files := make([]*source, 0, 5)
	for _, m := range googleFileRe.FindAllSubmatch(p, -1) {
		fname := string(m[1])
		if utils.IsDocFile(fname) {
			files = append(files, &source{
				name:      fname,
				browseURL: expand("http://code.google.com/p/{repo}/source/browse{dir}/{0}{query}", match, fname),
				rawURL:    expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/{0}", match, fname),
			})
		}
	}

	if len(files) == 0 {
		return nil, NotFoundError{"Directory tree does not contain Go files."}
	}

	dirs := make([]string, 0, 3)
	// Get subdirectories.
	for _, m := range googleDirRe.FindAllSubmatch(p, -1) {
		dirName := strings.Split(string(m[1]), "?")[0]
		if strings.HasSuffix(dirName, "/") {
			dirs = append(dirs, strings.Replace(dirName, "/", "", -1))
		}
	}

	// Fetch file from VCS.
	if err := fetchFiles(client, files, nil); err != nil {
		return nil, err
	}

	// Start generating data.
	w := &walker{
		lineFmt: "#%d",
		pdoc: &Package{
			ImportPath:  match["importPath"],
			ProjectName: expand("{repo}{dot}{subrepo}", match),
			Etag:        etag,
			Dirs:        dirs,
		},
	}
	return w.build(files)
}

func setupGoogleMatch(match map[string]string) {
	if s := match["subrepo"]; s != "" {
		match["dot"] = "."
		match["query"] = "?repo=" + s
	} else {
		match["dot"] = ""
		match["query"] = ""
	}
}

func getGoogleVCS(client *http.Client, match map[string]string) error {
	// Scrape the HTML project page to find the VCS.
	p, err := httpGetBytes(client, expand("http://code.google.com/p/{repo}/source/checkout", match), nil)
	if err != nil {
		return err
	}
	m := googleRepoRe.FindSubmatch(p)
	if m == nil {
		return NotFoundError{"Could not VCS on Google Code project page."}
	}
	match["vcs"] = string(m[1])
	return nil
}

// expand replaces {k} in template with match[k] or subs[atoi(k)] if k is not in match.
func expand(template string, match map[string]string, subs ...string) string {
	var p []byte
	var i int
	for {
		i = strings.Index(template, "{")
		if i < 0 {
			break
		}
		p = append(p, template[:i]...)
		template = template[i+1:]
		i = strings.Index(template, "}")
		if s, ok := match[template[:i]]; ok {
			p = append(p, s...)
		} else {
			j, _ := strconv.Atoi(template[:i])
			p = append(p, subs[j]...)
		}
		template = template[i+1:]
	}
	p = append(p, template...)
	return string(p)
}
<|endoftext|>