<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"sync"
)

type iDiffUtils interface {
	scanStdinToChannel(to chan string, cancel chan struct{})
}
type diffUtils struct{}

func scanToChannel(from io.Reader, to chan string, cancel chan struct{}) {
	scanner := bufio.NewScanner(from)
	intermediate := make(chan string)

	go func() {
		for scanner.Scan() {
			intermediate <- scanner.Text()
		}
		close(intermediate)
	}()

loop:
	for {
		select {
		case s, ok := <-intermediate:
			if !ok {
				break loop
			}
			to <- s
		case <-cancel:
			break loop
		}
	}
	close(to)
}

func (d diffUtils) scanStdinToChannel(i chan string, cancel chan struct{}) {
	scanToChannel(os.Stdin, i, cancel)
}

func readCmd(cmdString string, o chan string, cancel chan struct{}) {
	var stderr bytes.Buffer
	cmd := exec.Command("/bin/bash", "-c", cmdString)
	cmd.Stderr = &stderr

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}

	if err := cmd.Start(); err != nil {
		log.Fatal(err)
	}

	go func() {
		scanToChannel(stdout, o, cancel)
		cmd.Process.Kill()
	}()
}

func diffLine(v string, stdout chan string, diffee *[]string, start chan struct{}, intersection bool, wg *sync.WaitGroup) {
	<-start // wait until diffee finishes loading

	if intersection {
		for _, w := range *diffee {
			if v == w {
				stdout <- v
				break
			}
		}
	} else {
		found := false
		for _, w := range *diffee {
			if v == w {
				found = true
				break
			}
		}
		if !found {
			stdout <- v
		}
	}

	wg.Done()
}

func printLn(stdout chan string, done chan struct{}) {
	for s := range stdout {
		fmt.Println(s)
	}
	close(done)
}

func processStdin(
	stdinCh chan string,
	diffee *[]string,
	stdinTimeout timeout,
	cancelStdin chan struct{},
	start chan struct{},
	stdout chan string,
	intersection bool,
	wg *sync.WaitGroup) {

	stdinTimeout.Start()
	var innerWg sync.WaitGroup

loop:
	for {
		select {
		case s, ok := <-stdinCh:
			if !ok {
				break loop
			}
			innerWg.Add(1)
			go diffLine(s, stdout, diffee, start, intersection, &innerWg)
			stdinTimeout.Reset()
		case <-*stdinTimeout.c:
			close(cancelStdin)
			break loop
		}
	}

	innerWg.Wait()
	wg.Done()
}

func processCmd(cmdCh chan string, diffee *[]string, cmdTimeout timeout, cancelCmd chan struct{}, start chan struct{}, wg *sync.WaitGroup) {
	cmdTimeout.Start()
	for {
		select {
		case s, ok := <-cmdCh:
			if !ok {
				close(start)
				wg.Done()
				return
			}
			*diffee = append(*diffee, s)
			cmdTimeout.Reset()
		case <-*cmdTimeout.c:
			close(cancelCmd)
		}
	}
}

func diff(cmd string, stdinTimeout timeout, cmdTimeout timeout, stdout chan string, utils iDiffUtils, intersection bool) {
	var diffee []string

	stdinCh := make(chan string)
	cmdCh := make(chan string)
	start := make(chan struct{})
	cancelCmd := make(chan struct{})
	cancelStdin := make(chan struct{})

	go utils.scanStdinToChannel(stdinCh, cancelStdin)
	go readCmd(cmd, cmdCh, cancelCmd)

	var wg sync.WaitGroup
	wg.Add(2)

	go processStdin(stdinCh, &diffee, stdinTimeout, cancelStdin, start, stdout, intersection, &wg)
	go processCmd(cmdCh, &diffee, cmdTimeout, cancelCmd, start, &wg)

	wg.Wait()
	close(stdout)
}

var args []string

func main() {
	if len(os.Args) < 2 {
		usage()
	}
	args := os.Args[1:]

	options := mustResolveOptions(args)
	stdinTimeout, cmdTimeout := resolveTimeouts(options)
	cmd := os.Args[len(os.Args)-1]

	stdout := make(chan string)
	done := make(chan struct{})

	go printLn(stdout, done)
	diff(cmd, stdinTimeout, cmdTimeout, stdout, diffUtils{}, options.intersection)
	<-done
}
<commit_msg>Exits when showing usage.<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"sync"
)

type iDiffUtils interface {
	scanStdinToChannel(to chan string, cancel chan struct{})
}
type diffUtils struct{}

func scanToChannel(from io.Reader, to chan string, cancel chan struct{}) {
	scanner := bufio.NewScanner(from)
	intermediate := make(chan string)

	go func() {
		for scanner.Scan() {
			intermediate <- scanner.Text()
		}
		close(intermediate)
	}()

loop:
	for {
		select {
		case s, ok := <-intermediate:
			if !ok {
				break loop
			}
			to <- s
		case <-cancel:
			break loop
		}
	}
	close(to)
}

func (d diffUtils) scanStdinToChannel(i chan string, cancel chan struct{}) {
	scanToChannel(os.Stdin, i, cancel)
}

func readCmd(cmdString string, o chan string, cancel chan struct{}) {
	var stderr bytes.Buffer
	cmd := exec.Command("/bin/bash", "-c", cmdString)
	cmd.Stderr = &stderr

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}

	if err := cmd.Start(); err != nil {
		log.Fatal(err)
	}

	go func() {
		scanToChannel(stdout, o, cancel)
		cmd.Process.Kill()
	}()
}

func diffLine(v string, stdout chan string, diffee *[]string, start chan struct{}, intersection bool, wg *sync.WaitGroup) {
	<-start // wait until diffee finishes loading

	if intersection {
		for _, w := range *diffee {
			if v == w {
				stdout <- v
				break
			}
		}
	} else {
		found := false
		for _, w := range *diffee {
			if v == w {
				found = true
				break
			}
		}
		if !found {
			stdout <- v
		}
	}

	wg.Done()
}

func printLn(stdout chan string, done chan struct{}) {
	for s := range stdout {
		fmt.Println(s)
	}
	close(done)
}

func processStdin(
	stdinCh chan string,
	diffee *[]string,
	stdinTimeout timeout,
	cancelStdin chan struct{},
	start chan struct{},
	stdout chan string,
	intersection bool,
	wg *sync.WaitGroup) {

	stdinTimeout.Start()
	var innerWg sync.WaitGroup

loop:
	for {
		select {
		case s, ok := <-stdinCh:
			if !ok {
				break loop
			}
			innerWg.Add(1)
			go diffLine(s, stdout, diffee, start, intersection, &innerWg)
			stdinTimeout.Reset()
		case <-*stdinTimeout.c:
			close(cancelStdin)
			break loop
		}
	}

	innerWg.Wait()
	wg.Done()
}

func processCmd(cmdCh chan string, diffee *[]string, cmdTimeout timeout, cancelCmd chan struct{}, start chan struct{}, wg *sync.WaitGroup) {
	cmdTimeout.Start()
	for {
		select {
		case s, ok := <-cmdCh:
			if !ok {
				close(start)
				wg.Done()
				return
			}
			*diffee = append(*diffee, s)
			cmdTimeout.Reset()
		case <-*cmdTimeout.c:
			close(cancelCmd)
		}
	}
}

func diff(cmd string, stdinTimeout timeout, cmdTimeout timeout, stdout chan string, utils iDiffUtils, intersection bool) {
	var diffee []string

	stdinCh := make(chan string)
	cmdCh := make(chan string)
	start := make(chan struct{})
	cancelCmd := make(chan struct{})
	cancelStdin := make(chan struct{})

	go utils.scanStdinToChannel(stdinCh, cancelStdin)
	go readCmd(cmd, cmdCh, cancelCmd)

	var wg sync.WaitGroup
	wg.Add(2)

	go processStdin(stdinCh, &diffee, stdinTimeout, cancelStdin, start, stdout, intersection, &wg)
	go processCmd(cmdCh, &diffee, cmdTimeout, cancelCmd, start, &wg)

	wg.Wait()
	close(stdout)
}

var args []string

func main() {
	if len(os.Args) < 2 {
		usage()
		os.Exit(1)
	}
	args := os.Args[1:]

	options := mustResolveOptions(args)
	stdinTimeout, cmdTimeout := resolveTimeouts(options)
	cmd := os.Args[len(os.Args)-1]

	stdout := make(chan string)
	done := make(chan struct{})

	go printLn(stdout, done)
	diff(cmd, stdinTimeout, cmdTimeout, stdout, diffUtils{}, options.intersection)
	<-done
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc.
//
// 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.

// This program ensures source code files have copyright license headers.
// See usage with "addlicence -h".
package main

import (
	"bytes"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

const helpText = `Usage: addlicense [flags] pattern [pattern ...]

The program ensures source code files have copyright license headers
by scanning directory patterns recursively.

It modifies all source files in place and avoids adding a license header
to any file that already has one.

The pattern argument can be provided multiple times, and may also refer
to single files.

Flags:
`

var (
	holder   = flag.String("c", "Google LLC", "copyright holder")
	license  = flag.String("l", "apache", "license type: apache, bsd, mit")
	licensef = flag.String("f", "", "license file")
	year     = flag.Int("y", time.Now().Year(), "year")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, helpText)
		flag.PrintDefaults()
	}
	flag.Parse()
	if flag.NArg() == 0 {
		flag.Usage()
		os.Exit(1)
	}

	data := &copyrightData{
		Year:   *year,
		Holder: *holder,
	}

	var t *template.Template
	if *licensef != "" {
		d, err := ioutil.ReadFile(*licensef)
		if err != nil {
			log.Printf("template error: %s", err)
			os.Exit(1)
		}
		t, err = template.New("").Parse(string(d))
		if err != nil {
			log.Printf("template error: %s", err)
			os.Exit(1)
		}
	} else {
		t = licenseTemplate[*license]
		if t == nil {
			log.Printf("unknown license: %s", *license)
			os.Exit(1)
		}
	}

	// process at most 1000 files in parallel
	ch := make(chan *file, 1000)
	done := make(chan struct{})
	go func() {
		var wg sync.WaitGroup
		for f := range ch {
			wg.Add(1)
			go func(f *file) {
				err := addLicense(f.path, f.mode, t, data)
				if err != nil {
					log.Printf("%s: %v", f.path, err)
				}
				wg.Done()
			}(f)
		}
		wg.Wait()
		close(done)
	}()

	for _, d := range flag.Args() {
		walk(ch, d)
	}
	close(ch)
	<-done
}

type file struct {
	path string
	mode os.FileMode
}

func walk(ch chan<- *file, start string) {
	filepath.Walk(start, func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Printf("%s error: %v", path, err)
			return nil
		}
		if fi.IsDir() {
			return nil
		}
		ch <- &file{path, fi.Mode()}
		return nil
	})
}

func addLicense(path string, fmode os.FileMode, tmpl *template.Template, data *copyrightData) error {
	var lic []byte
	var err error
	switch filepath.Ext(path) {
	default:
		return nil
	case ".c", ".h":
		lic, err = prefix(tmpl, data, "/*", " * ", " */")
	case ".js", ".jsx", ".tsx", ".css", ".tf":
		lic, err = prefix(tmpl, data, "/**", " * ", " */")
	case ".cc", ".cpp", ".cs", ".go", ".hh", ".hpp", ".java", ".m", ".mm", ".proto", ".rs", ".scala", ".swift", ".dart":
		lic, err = prefix(tmpl, data, "", "// ", "")
	case ".py", ".sh", ".yaml", ".yml":
		lic, err = prefix(tmpl, data, "", "# ", "")
	case ".el", ".lisp":
		lic, err = prefix(tmpl, data, "", ";; ", "")
	case ".erl":
		lic, err = prefix(tmpl, data, "", "% ", "")
	case ".hs", ".sql":
		lic, err = prefix(tmpl, data, "", "-- ", "")
	case ".html", ".xml":
		lic, err = prefix(tmpl, data, "<!--", " ", "-->")
	case ".php":
		lic, err = prefix(tmpl, data, "<?php", "// ", "?>")
	}
	if err != nil || lic == nil {
		return err
	}

	b, err := ioutil.ReadFile(path)
	if err != nil || hasLicense(b) {
		return err
	}

	line := hashBang(b)
	if len(line) > 0 {
		b = b[len(line):]
		if line[len(line)-1] != '\n' {
			line = append(line, '\n')
		}
		lic = append(line, lic...)
	}
	b = append(lic, b...)
	return ioutil.WriteFile(path, b, fmode)
}

var head = []string{
	"#!",        // shell script
	"<?xml",     // XML declaratioon
	"<!doctype", // HTML doctype
}

func hashBang(b []byte) []byte {
	var line []byte
	for _, c := range b {
		line = append(line, c)
		if c == '\n' {
			break
		}
	}
	first := strings.ToLower(string(line))
	for _, h := range head {
		if strings.HasPrefix(first, h) {
			return line
		}
	}
	return nil
}

func hasLicense(b []byte) bool {
	n := 1000
	if len(b) < 1000 {
		n = len(b)
	}
	return bytes.Contains(bytes.ToLower(b[:n]), []byte("copyright"))
}
<commit_msg>update error prefix<commit_after>// Copyright 2016 Google Inc.
//
// 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.

// This program ensures source code files have copyright license headers.
// See usage with "addlicence -h".
package main

import (
	"bytes"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

const helpText = `Usage: addlicense [flags] pattern [pattern ...]

The program ensures source code files have copyright license headers
by scanning directory patterns recursively.

It modifies all source files in place and avoids adding a license header
to any file that already has one.

The pattern argument can be provided multiple times, and may also refer
to single files.

Flags:
`

var (
	holder   = flag.String("c", "Google LLC", "copyright holder")
	license  = flag.String("l", "apache", "license type: apache, bsd, mit")
	licensef = flag.String("f", "", "license file")
	year     = flag.Int("y", time.Now().Year(), "year")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, helpText)
		flag.PrintDefaults()
	}
	flag.Parse()
	if flag.NArg() == 0 {
		flag.Usage()
		os.Exit(1)
	}

	data := &copyrightData{
		Year:   *year,
		Holder: *holder,
	}

	var t *template.Template
	if *licensef != "" {
		d, err := ioutil.ReadFile(*licensef)
		if err != nil {
			log.Printf("license file: %s", err)
			os.Exit(1)
		}
		t, err = template.New("").Parse(string(d))
		if err != nil {
			log.Printf("license file: %s", err)
			os.Exit(1)
		}
	} else {
		t = licenseTemplate[*license]
		if t == nil {
			log.Printf("unknown license: %s", *license)
			os.Exit(1)
		}
	}

	// process at most 1000 files in parallel
	ch := make(chan *file, 1000)
	done := make(chan struct{})
	go func() {
		var wg sync.WaitGroup
		for f := range ch {
			wg.Add(1)
			go func(f *file) {
				err := addLicense(f.path, f.mode, t, data)
				if err != nil {
					log.Printf("%s: %v", f.path, err)
				}
				wg.Done()
			}(f)
		}
		wg.Wait()
		close(done)
	}()

	for _, d := range flag.Args() {
		walk(ch, d)
	}
	close(ch)
	<-done
}

type file struct {
	path string
	mode os.FileMode
}

func walk(ch chan<- *file, start string) {
	filepath.Walk(start, func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Printf("%s error: %v", path, err)
			return nil
		}
		if fi.IsDir() {
			return nil
		}
		ch <- &file{path, fi.Mode()}
		return nil
	})
}

func addLicense(path string, fmode os.FileMode, tmpl *template.Template, data *copyrightData) error {
	var lic []byte
	var err error
	switch filepath.Ext(path) {
	default:
		return nil
	case ".c", ".h":
		lic, err = prefix(tmpl, data, "/*", " * ", " */")
	case ".js", ".jsx", ".tsx", ".css", ".tf":
		lic, err = prefix(tmpl, data, "/**", " * ", " */")
	case ".cc", ".cpp", ".cs", ".go", ".hh", ".hpp", ".java", ".m", ".mm", ".proto", ".rs", ".scala", ".swift", ".dart":
		lic, err = prefix(tmpl, data, "", "// ", "")
	case ".py", ".sh", ".yaml", ".yml":
		lic, err = prefix(tmpl, data, "", "# ", "")
	case ".el", ".lisp":
		lic, err = prefix(tmpl, data, "", ";; ", "")
	case ".erl":
		lic, err = prefix(tmpl, data, "", "% ", "")
	case ".hs", ".sql":
		lic, err = prefix(tmpl, data, "", "-- ", "")
	case ".html", ".xml":
		lic, err = prefix(tmpl, data, "<!--", " ", "-->")
	case ".php":
		lic, err = prefix(tmpl, data, "<?php", "// ", "?>")
	}
	if err != nil || lic == nil {
		return err
	}

	b, err := ioutil.ReadFile(path)
	if err != nil || hasLicense(b) {
		return err
	}

	line := hashBang(b)
	if len(line) > 0 {
		b = b[len(line):]
		if line[len(line)-1] != '\n' {
			line = append(line, '\n')
		}
		lic = append(line, lic...)
	}
	b = append(lic, b...)
	return ioutil.WriteFile(path, b, fmode)
}

var head = []string{
	"#!",        // shell script
	"<?xml",     // XML declaratioon
	"<!doctype", // HTML doctype
}

func hashBang(b []byte) []byte {
	var line []byte
	for _, c := range b {
		line = append(line, c)
		if c == '\n' {
			break
		}
	}
	first := strings.ToLower(string(line))
	for _, h := range head {
		if strings.HasPrefix(first, h) {
			return line
		}
	}
	return nil
}

func hasLicense(b []byte) bool {
	n := 1000
	if len(b) < 1000 {
		n = len(b)
	}
	return bytes.Contains(bytes.ToLower(b[:n]), []byte("copyright"))
}
<|endoftext|>


<commit_before>package main

/**
 * Imports
 */
import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	// "runtime"
	"strings"
	"time"
)

/**
 * Dependency type
 */
type Dependency struct {
	Name, Version string
}

/**
 * Options type
 */
type Options struct {
	GopasFile  string
	Watches    []string
	Ignores    []string
	Extensions []string
}

/**
 * Runner type
 */
type Runner struct {
	file    string
	command *exec.Cmd
}

/**
 * ScanCallback type
 */
type ScanCallback func(path string)

/**
 * Global variables
 */
var (
	options      Options
	modifiedTime time.Time = time.Now()
	cwd          string    = getCwd()
)

/**
 * Run command
 *
 * @return {*exec.Cmd}
 * @return {error}
 */
func (r *Runner) Run() (*exec.Cmd, error) {
	if r.command == nil || r.IsExited() {
		err := r.runBin()
		// time.Sleep(250 * time.Millisecond)
		return r.command, err
	} else {
		return r.command, nil
	}

}

/**
 * Check wether runner command already exit
 *
 * @return {bool}
 */
func (r *Runner) IsExited() bool {
	return r.command != nil && r.command.ProcessState != nil && r.command.ProcessState.Exited()
}

/**
 * Run bin
 *
 * @return {error}
 */
func (r *Runner) runBin() error {
	// cwd := cwd()
	fmt.Printf(">> Running \"go run %s\"\n", r.file)

	r.command = exec.Command("go", "run", r.file)
	// r.command = exec.Command("go", "env")
	env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOPATH=") {
			env = append(env, v)
		}
	}
	r.command.Env = env
	r.command.Dir = cwd + "/.gopath/src/" + filepath.Base(cwd)

	stdout, err := r.command.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := r.command.StderrPipe()
	if err != nil {
		return err
	}

	err = r.command.Start()
	if err != nil {
		return err
	}

	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stderr, stderr)
	go r.command.Wait()

	return nil
}

/**
 * Kill runner command
 *
 * @return {error}
 */
func (r *Runner) Kill() error {
	if r.command != nil && r.command.Process != nil {
		done := make(chan error)
		go func() {
			r.command.Wait()
			close(done)
		}()

		//Trying a "soft" kill first
		// if runtime.GOOS == "windows" {
		if err := r.command.Process.Kill(); err != nil {
			return err
		}
		// } else if err := r.command.Process.Signal(os.Interrupt); err != nil {
		// 	return err
		// }

		//Wait for our process to die before we return or hard kill after 3 sec
		select {
		case <-time.After(3 * time.Second):
			if err := r.command.Process.Kill(); err != nil {
				fmt.Fprintln(os.Stderr, ">> Kill Error: ", err)
				os.Exit(1)
			}
		case <-done:
		}
		r.command = nil
	}

	return nil
}

/**
 * Read dependencies listed on filename
 *
 * @param {string} filename
 * @return {[]Dependency}
 */
func readDependencies(filename string) []Dependency {
	dependencies := []Dependency{}

	fileBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return dependencies
	}

	fileLines := strings.Split(string(fileBytes), "\n")
	for _, line := range fileLines {
		if line != "" {
			token := strings.Split(line, "=")

			dependencies = append(dependencies, Dependency{
				Name:    strings.Trim(token[0], " \t"),
				Version: strings.Trim(token[1], " \t"),
			})
		}
	}

	return dependencies
}

/**
 * Get current working directory
 *
 * @return {string}
 */
func getCwd() string {
	cwd, err := os.Getwd()
	if err != nil {
		fmt.Fprintf(os.Stderr, ">> Getwd Error: %s\n", err.Error())
		os.Exit(1)
	}
	return cwd
}

/**
 * Check whether path is ignorable
 *
 * @param {string} path
 */
func isIgnorable(path string) bool {
	ignorable := false
	for _, ignore := range options.Ignores {
		if matched, _ := filepath.Match(ignore, path); matched {
			ignorable = true
			break
		}
	}
	return ignorable
}

/**
 * Check whether path having acceptable extension
 *
 * @param {string} path
 */
func isAcceptable(path string) bool {
	for _, ext := range options.Extensions {
		if strings.HasSuffix(path, "."+ext) {
			return true
		}
	}

	return false
}

/**
 * Scan changes and invoke callback on file changes
 *
 * @param  {ScanCallback}   cb ScanCallback
 */
func scanChanges(cb ScanCallback) {
	for {
		for _, dir := range options.Watches {
			filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
				if isIgnorable(path) {
					return filepath.SkipDir
				}

				if isAcceptable(path) && info.ModTime().After(modifiedTime) {
					modifiedTime = time.Now()
					cb(path)
				}
				return nil
			})
		}

		time.Sleep(500 * time.Millisecond)
	}
}

/**
 * List action
 */
func actionList() {
	depCount := 0
	dependencies := readDependencies(options.GopasFile)
	for _, dep := range dependencies {
		fmt.Printf("%s %s\n", dep.Name, dep.Version)
		depCount++
	}
	fmt.Printf("dependencies(%d)\n", depCount)
}

/**
 * Install action
 */
func actionInstall() {
	dependencies := readDependencies(options.GopasFile)
	for _, dep := range dependencies {
		cmd := exec.Command("go", "get", dep.Name)
		env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
		for _, v := range os.Environ() {
			if !strings.HasPrefix(v, "GOPATH=") {
				env = append(env, v)
			}
		}
		cmd.Env = env
		cmd.Start()
		if err := cmd.Wait(); err != nil {
			fmt.Fprintf(os.Stderr, ">> Install Error: %s\n", err.Error())
			os.Exit(1)
		}
		fmt.Printf(">> Installing \"go get %s\"\n", dep.Name)
	}
}

/**
 * Help action
 */
func actionHelp() {
	fmt.Println("Gopas is a tool to build Go outside GOPATH")
	fmt.Println("")
	fmt.Println("Usage:")
	fmt.Println("")
	fmt.Println("  gopas <action> [<args...>]")
	fmt.Println("")
	fmt.Println("The actions are:")
	fmt.Println("")
	fmt.Println("  build    compile packages and dependencies")
	fmt.Println("  help     show help")
	fmt.Println("  install  compile and install packages and dependencies")
	fmt.Println("  list     list dependencies")
	fmt.Println("  run      compile and run Go program")
	fmt.Println("  test     test packages")
	fmt.Println("")
}

/**
 * Run action
 */
func actionRun() {
	if len(os.Args) < 3 {
		fmt.Fprintln(os.Stderr, "Usage: gopas run <file>")
		os.Exit(1)
	}

	file := os.Args[2]

	if _, err := os.Stat(file); os.IsNotExist(err) {
		fmt.Fprintf(os.Stderr, "File \"%s\" is not exists\n", file)
		os.Exit(1)
	}

	runner := &Runner{
		file: file,
	}

	fmt.Println(">> Watches    ", strings.Join(options.Watches, ", "))
	fmt.Println(">> Extensions ", strings.Join(options.Extensions, ", "))
	fmt.Println(">> Ignores    ", strings.Join(options.Ignores, ", "))

	runner.Run()
	go scanChanges(func(path string) {
		runner.Kill()
		bootstrap()
		time.Sleep(250 * time.Millisecond)
		runner.Run()
	})

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		if scanner.Text() == "rs" {
			time.Sleep(700 * time.Millisecond)
			runner.Kill()
			bootstrap()
			time.Sleep(250 * time.Millisecond)
			runner.Run()
		}
	}
}

/**
 * Test action
 */
func actionTest() {
	// cwd := cwd()
	cmd := exec.Command("go", "test")
	cmd.Dir = cwd + "/.gopath/src/" + filepath.Base(cwd)
	env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOPATH=") {
			env = append(env, v)
		}
	}
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Start()
	cmd.Wait()
}

/**
 * Build action
 */
func actionBuild() {
	// cwd := cwd()
	cmd := exec.Command("go", "build")
	cmd.Dir = cwd + "/.gopath/src/" + filepath.Base(cwd)
	env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOPATH=") {
			env = append(env, v)
		}
	}
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Start()
	cmd.Wait()
}

/**
 * Bootstrap project options and gopath dir
 */
func bootstrap() {
	fmt.Println(">> Bootstrapping...")
	if _, err := os.Stat(".gopath"); os.IsNotExist(err) {
		if os.MkdirAll(".gopath/src", 0755) != nil {
			fmt.Fprintf(os.Stderr, ">> Bootstrap Error: %s\n", err.Error())
			os.Exit(1)
		}
	}

	if err := os.RemoveAll(cwd + "/.gopath/src/" + filepath.Base(cwd)); err != nil {
		fmt.Fprintf(os.Stderr, ">> Bootstrap Error: %s\n", err.Error())
	}
	copy_folder(cwd, cwd+"/.gopath/src/"+filepath.Base(cwd))

	// os.Symlink(cwd, cwd+"/.gopath/src/"+filepath.Base(cwd))
	// // FIXME workaround, cannot read module on vendor dir
	// if files, err := ioutil.ReadDir("vendor"); err == nil {
	// 	for _, file := range files {
	// 		name := file.Name()
	// 		// os.RemoveAll(cwd + "/.gopath/src/" + name)
	// 		os.Symlink(cwd+"/vendor/"+name, cwd+"/.gopath/src/"+name)
	// 	}
	// }
}

/**
 * main function
 */
func main() {
	options = Options{
		GopasFile:  "gopasfile",
		Watches:    []string{"."},
		Ignores:    []string{".git", ".gopath"},
		Extensions: []string{"go"},
	}

	bootstrap()

	if len(os.Args) > 1 {
		switch os.Args[1] {
		case "list":
			actionList()
			return
		case "install":
			actionInstall()
			return
		case "run":
			actionRun()
			return
		case "build":
			actionBuild()
			return
		case "test":
			actionTest()
			return
		}
	}
	actionHelp()
}

func copy_folder(source string, dest string) (err error) {

	sourceinfo, err := os.Stat(source)
	if err != nil {
		return err
	}

	err = os.MkdirAll(dest, sourceinfo.Mode())
	if err != nil {
		return err
	}

	directory, _ := os.Open(source)

	objects, err := directory.Readdir(-1)

	for _, obj := range objects {

		sourcefilepointer := source + "/" + obj.Name()

		destinationfilepointer := dest + "/" + obj.Name()

		if obj.IsDir() {
			if obj.Name() != ".gopath" {
				err = copy_folder(sourcefilepointer, destinationfilepointer)
				if err != nil {
					fmt.Println(err)
				}
			}
		} else {
			err = copy_file(sourcefilepointer, destinationfilepointer)
			if err != nil {
				fmt.Println(err)
			}
		}

	}
	return
}

func copy_file(source string, dest string) (err error) {
	sourcefile, err := os.Open(source)
	if err != nil {
		return err
	}

	defer sourcefile.Close()

	destfile, err := os.Create(dest)
	if err != nil {
		return err
	}

	defer destfile.Close()

	_, err = io.Copy(destfile, sourcefile)
	if err == nil {
		sourceinfo, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, sourceinfo.Mode())
		}

	}

	return
}
<commit_msg>FIXED go run not working on rs restart<commit_after>package main

/**
 * Imports
 */
import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	// "runtime"
	"strings"
	"time"
)

/**
 * Dependency type
 */
type Dependency struct {
	Name, Version string
}

/**
 * Options type
 */
type Options struct {
	GopasFile  string
	Watches    []string
	Ignores    []string
	Extensions []string
}

/**
 * Runner type
 */
type Runner struct {
	name    string
	args    []string
	command *exec.Cmd
}

/**
 * ScanCallback type
 */
type ScanCallback func(path string)

/**
 * Global variables
 */
var (
	options      Options
	modifiedTime time.Time = time.Now()
	cwd          string    = getCwd()
)

/**
 * Run command
 *
 * @return {*exec.Cmd}
 * @return {error}
 */
func (r *Runner) Run() (*exec.Cmd, error) {
	if r.command == nil || r.IsExited() {
		err := r.runBin()
		// time.Sleep(250 * time.Millisecond)
		return r.command, err
	} else {
		return r.command, nil
	}

}

/**
 * Check wether runner command already exit
 *
 * @return {bool}
 */
func (r *Runner) IsExited() bool {
	return r.command != nil && r.command.ProcessState != nil && r.command.ProcessState.Exited()
}

/**
 * Run bin
 *
 * @return {error}
 */
func (r *Runner) runBin() error {
	// cwd := cwd()
	fmt.Println(">> Running", r.name, r.args)

	r.command = exec.Command(r.name, r.args...)
	// r.command = exec.Command("go", "env")
	env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOPATH=") {
			env = append(env, v)
		}
	}
	r.command.Env = env
	r.command.Dir = cwd + "/.gopath/src/" + filepath.Base(cwd)

	stdout, err := r.command.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := r.command.StderrPipe()
	if err != nil {
		return err
	}

	err = r.command.Start()
	if err != nil {
		return err
	}

	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stderr, stderr)
	go r.command.Wait()

	return nil
}

/**
 * Kill runner command
 *
 * @return {error}
 */
func (r *Runner) Kill() error {
	if r.command != nil && r.command.Process != nil {
		done := make(chan error)
		go func() {
			r.command.Wait()
			close(done)
		}()

		//Trying a "soft" kill first
		// if runtime.GOOS == "windows" {
		if err := r.command.Process.Kill(); err != nil {
			return err
		}
		// } else if err := r.command.Process.Signal(os.Interrupt); err != nil {
		// 	return err
		// }

		//Wait for our process to die before we return or hard kill after 3 sec
		select {
		case <-time.After(3 * time.Second):
			if err := r.command.Process.Kill(); err != nil {
				fmt.Fprintln(os.Stderr, ">> Kill Error: ", err)
				os.Exit(1)
			}
		case <-done:
		}
		r.command = nil
	}

	return nil
}

/**
 * Read dependencies listed on filename
 *
 * @param {string} filename
 * @return {[]Dependency}
 */
func readDependencies(filename string) []Dependency {
	dependencies := []Dependency{}

	fileBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return dependencies
	}

	fileLines := strings.Split(string(fileBytes), "\n")
	for _, line := range fileLines {
		if line != "" {
			token := strings.Split(line, "=")

			dependencies = append(dependencies, Dependency{
				Name:    strings.Trim(token[0], " \t"),
				Version: strings.Trim(token[1], " \t"),
			})
		}
	}

	return dependencies
}

/**
 * Get current working directory
 *
 * @return {string}
 */
func getCwd() string {
	cwd, err := os.Getwd()
	if err != nil {
		fmt.Fprintf(os.Stderr, ">> Getwd Error: %s\n", err.Error())
		os.Exit(1)
	}
	return cwd
}

/**
 * Check whether path is ignorable
 *
 * @param {string} path
 */
func isIgnorable(path string) bool {
	ignorable := false
	for _, ignore := range options.Ignores {
		if matched, _ := filepath.Match(ignore, path); matched {
			ignorable = true
			break
		}
	}
	return ignorable
}

/**
 * Check whether path having acceptable extension
 *
 * @param {string} path
 */
func isAcceptable(path string) bool {
	for _, ext := range options.Extensions {
		if strings.HasSuffix(path, "."+ext) {
			return true
		}
	}

	return false
}

/**
 * Scan changes and invoke callback on file changes
 *
 * @param  {ScanCallback}   cb ScanCallback
 */
func scanChanges(cb ScanCallback) {
	for {
		for _, dir := range options.Watches {
			filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
				if isIgnorable(path) {
					return filepath.SkipDir
				}

				if isAcceptable(path) && info.ModTime().After(modifiedTime) {
					modifiedTime = time.Now()
					cb(path)
				}
				return nil
			})
		}

		time.Sleep(500 * time.Millisecond)
	}
}

/**
 * List action
 */
func actionList() {
	depCount := 0
	dependencies := readDependencies(options.GopasFile)
	for _, dep := range dependencies {
		fmt.Printf("%s %s\n", dep.Name, dep.Version)
		depCount++
	}
	fmt.Printf("dependencies(%d)\n", depCount)
}

/**
 * Install action
 */
func actionInstall() {
	dependencies := readDependencies(options.GopasFile)
	for _, dep := range dependencies {
		cmd := exec.Command("go", "get", dep.Name)
		env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
		for _, v := range os.Environ() {
			if !strings.HasPrefix(v, "GOPATH=") {
				env = append(env, v)
			}
		}
		cmd.Env = env
		cmd.Start()
		if err := cmd.Wait(); err != nil {
			fmt.Fprintf(os.Stderr, ">> Install Error: %s\n", err.Error())
			os.Exit(1)
		}
		fmt.Printf(">> Installing \"go get %s\"\n", dep.Name)
	}
}

/**
 * Help action
 */
func actionHelp() {
	fmt.Println("Gopas is a tool to build Go outside GOPATH")
	fmt.Println("")
	fmt.Println("Usage:")
	fmt.Println("")
	fmt.Println("  gopas <action> [<args...>]")
	fmt.Println("")
	fmt.Println("The actions are:")
	fmt.Println("")
	fmt.Println("  build    compile packages and dependencies")
	fmt.Println("  help     show help")
	fmt.Println("  install  compile and install packages and dependencies")
	fmt.Println("  list     list dependencies")
	fmt.Println("  run      compile and run Go program")
	fmt.Println("  test     test packages")
	fmt.Println("")
}

/**
 * Run action
 */
func actionRun() {
	if len(os.Args) < 3 {
		fmt.Fprintln(os.Stderr, "Usage: gopas run <file>")
		os.Exit(1)
	}

	for _, file := range os.Args[2:] {
		if _, err := os.Stat(file); os.IsNotExist(err) {
			fmt.Fprintf(os.Stderr, "File \"%s\" is not exists\n", file)
			os.Exit(1)
		}
	}

	runner := &Runner{
		name: "go",
		args: append([]string{"run"}, os.Args[2:]...),
	}

	fmt.Println(">> Watches    ", strings.Join(options.Watches, ", "))
	fmt.Println(">> Extensions ", strings.Join(options.Extensions, ", "))
	fmt.Println(">> Ignores    ", strings.Join(options.Ignores, ", "))

	runner.Run()
	go scanChanges(func(path string) {
		runner.Kill()
		bootstrap()
		time.Sleep(250 * time.Millisecond)
		runner.Run()
	})

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		if scanner.Text() == "rs" {
			time.Sleep(700 * time.Millisecond)
			runner.Kill()
			bootstrap()
			time.Sleep(250 * time.Millisecond)
			runner.Run()
		}
	}
}

/**
 * Test action
 */
func actionTest() {
	// cwd := cwd()
	cmd := exec.Command("go", "test")
	cmd.Dir = cwd + "/.gopath/src/" + filepath.Base(cwd)
	env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOPATH=") {
			env = append(env, v)
		}
	}
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Start()
	cmd.Wait()
}

/**
 * Build action
 */
func actionBuild() {
	// cwd := cwd()
	cmd := exec.Command("go", "build")
	cmd.Dir = cwd + "/.gopath/src/" + filepath.Base(cwd)
	env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOPATH=") {
			env = append(env, v)
		}
	}
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Start()
	cmd.Wait()
}

/**
 * Bootstrap project options and gopath dir
 */
func bootstrap() {
	fmt.Println(">> Bootstrapping...")
	if _, err := os.Stat(".gopath"); os.IsNotExist(err) {
		if os.MkdirAll(".gopath/src", 0755) != nil {
			fmt.Fprintf(os.Stderr, ">> Bootstrap Error: %s\n", err.Error())
			os.Exit(1)
		}
	}

	if err := os.RemoveAll(cwd + "/.gopath/src/" + filepath.Base(cwd)); err != nil {
		fmt.Fprintf(os.Stderr, ">> Bootstrap Error: %s\n", err.Error())
	}
	copy_folder(cwd, cwd+"/.gopath/src/"+filepath.Base(cwd))

	// os.Symlink(cwd, cwd+"/.gopath/src/"+filepath.Base(cwd))
	// // FIXME workaround, cannot read module on vendor dir
	// if files, err := ioutil.ReadDir("vendor"); err == nil {
	// 	for _, file := range files {
	// 		name := file.Name()
	// 		// os.RemoveAll(cwd + "/.gopath/src/" + name)
	// 		os.Symlink(cwd+"/vendor/"+name, cwd+"/.gopath/src/"+name)
	// 	}
	// }
}

/**
 * main function
 */
func main() {
	options = Options{
		GopasFile:  "gopasfile",
		Watches:    []string{"."},
		Ignores:    []string{".git", ".gopath"},
		Extensions: []string{"go"},
	}

	bootstrap()

	if len(os.Args) > 1 {
		switch os.Args[1] {
		case "list":
			actionList()
			return
		case "install":
			actionInstall()
			return
		case "run":
			actionRun()
			return
		case "build":
			actionBuild()
			return
		case "test":
			actionTest()
			return
		}
	}
	actionHelp()
}

func copy_folder(source string, dest string) (err error) {

	sourceinfo, err := os.Stat(source)
	if err != nil {
		return err
	}

	err = os.MkdirAll(dest, sourceinfo.Mode())
	if err != nil {
		return err
	}

	directory, _ := os.Open(source)

	objects, err := directory.Readdir(-1)

	for _, obj := range objects {

		sourcefilepointer := source + "/" + obj.Name()

		destinationfilepointer := dest + "/" + obj.Name()

		if obj.IsDir() {
			if obj.Name() != ".gopath" {
				err = copy_folder(sourcefilepointer, destinationfilepointer)
				if err != nil {
					fmt.Println(err)
				}
			}
		} else {
			err = copy_file(sourcefilepointer, destinationfilepointer)
			if err != nil {
				fmt.Println(err)
			}
		}

	}
	return
}

func copy_file(source string, dest string) (err error) {
	sourcefile, err := os.Open(source)
	if err != nil {
		return err
	}

	defer sourcefile.Close()

	destfile, err := os.Create(dest)
	if err != nil {
		return err
	}

	defer destfile.Close()

	_, err = io.Copy(destfile, sourcefile)
	if err == nil {
		sourceinfo, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, sourceinfo.Mode())
		}

	}

	return
}
<|endoftext|>


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

package main

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

func buildObjdump(t *testing.T) (tmp, exe string) {
	switch runtime.GOOS {
	case "android", "nacl":
		t.Skipf("skipping on %s", runtime.GOOS)
	case "darwin":
		if runtime.GOARCH == "arm" {
			t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
		}
	}

	tmp, err := ioutil.TempDir("", "TestObjDump")
	if err != nil {
		t.Fatal("TempDir failed: ", err)
	}

	exe = filepath.Join(tmp, "testobjdump.exe")
	out, err := exec.Command("go", "build", "-o", exe, "cmd/objdump").CombinedOutput()
	if err != nil {
		os.RemoveAll(tmp)
		t.Fatalf("go build -o %v cmd/objdump: %v\n%s", exe, err, string(out))
	}
	return
}

var x86Need = []string{
	"fmthello.go:6",
	"TEXT main.main(SB)",
	"JMP main.main(SB)",
	"CALL fmt.Println(SB)",
	"RET",
}

var armNeed = []string{
	"fmthello.go:6",
	"TEXT main.main(SB)",
	//"B.LS main.main(SB)", // TODO(rsc): restore; golang.org/issue/9021
	"BL fmt.Println(SB)",
	"RET",
}

// objdump is fully cross platform: it can handle binaries
// from any known operating system and architecture.
// We could in principle add binaries to testdata and check
// all the supported systems during this test. However, the
// binaries would be about 1 MB each, and we don't want to
// add that much junk to the hg repository. Instead, build a
// binary for the current system (only) and test that objdump
// can handle that one.

func testDisasm(t *testing.T, flags ...string) {
	tmp, exe := buildObjdump(t)
	defer os.RemoveAll(tmp)

	hello := filepath.Join(tmp, "hello.exe")
	args := []string{"build", "-o", hello}
	args = append(args, flags...)
	args = append(args, "testdata/fmthello.go")
	out, err := exec.Command("go", args...).CombinedOutput()
	if err != nil {
		t.Fatalf("go build fmthello.go: %v\n%s", err, out)
	}
	need := []string{
		"fmthello.go:6",
		"TEXT main.main(SB)",
	}
	switch runtime.GOARCH {
	case "amd64", "386":
		need = append(need, x86Need...)
	case "arm":
		need = append(need, armNeed...)
	}

	out, err = exec.Command(exe, "-s", "main.main", hello).CombinedOutput()
	if err != nil {
		t.Fatalf("objdump fmthello.exe: %v\n%s", err, out)
	}

	text := string(out)
	ok := true
	for _, s := range need {
		if !strings.Contains(text, s) {
			t.Errorf("disassembly missing '%s'", s)
			ok = false
		}
	}
	if !ok {
		t.Logf("full disassembly:\n%s", text)
	}
}

func TestDisasm(t *testing.T) {
	switch runtime.GOARCH {
	case "ppc64", "ppc64le":
		t.Skipf("skipping on %s, issue 9039", runtime.GOARCH)
	case "arm64":
		t.Skipf("skipping on %s, issue 10106", runtime.GOARCH)
	}
	testDisasm(t)
}

func TestDisasmExtld(t *testing.T) {
	switch runtime.GOOS {
	case "plan9", "windows":
		t.Skipf("skipping on %s", runtime.GOOS)
	}
	switch runtime.GOARCH {
	case "ppc64", "ppc64le":
		t.Skipf("skipping on %s, no support for external linking, issue 9038", runtime.GOARCH)
	case "arm64":
		t.Skipf("skipping on %s, issue 10106", runtime.GOARCH)
	}
	testDisasm(t, "-ldflags=-linkmode=external")
}
<commit_msg>cmd/objdump: skip fork test on darwin/arm64<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

func buildObjdump(t *testing.T) (tmp, exe string) {
	switch runtime.GOOS {
	case "android", "nacl":
		t.Skipf("skipping on %s", runtime.GOOS)
	case "darwin":
		switch runtime.GOARCH {
		case "arm", "arm64":
			t.Skipf("skipping on %s/%s, cannot fork", runtime.GOOS, runtime.GOARCH)
		}
	}

	tmp, err := ioutil.TempDir("", "TestObjDump")
	if err != nil {
		t.Fatal("TempDir failed: ", err)
	}

	exe = filepath.Join(tmp, "testobjdump.exe")
	out, err := exec.Command("go", "build", "-o", exe, "cmd/objdump").CombinedOutput()
	if err != nil {
		os.RemoveAll(tmp)
		t.Fatalf("go build -o %v cmd/objdump: %v\n%s", exe, err, string(out))
	}
	return
}

var x86Need = []string{
	"fmthello.go:6",
	"TEXT main.main(SB)",
	"JMP main.main(SB)",
	"CALL fmt.Println(SB)",
	"RET",
}

var armNeed = []string{
	"fmthello.go:6",
	"TEXT main.main(SB)",
	//"B.LS main.main(SB)", // TODO(rsc): restore; golang.org/issue/9021
	"BL fmt.Println(SB)",
	"RET",
}

// objdump is fully cross platform: it can handle binaries
// from any known operating system and architecture.
// We could in principle add binaries to testdata and check
// all the supported systems during this test. However, the
// binaries would be about 1 MB each, and we don't want to
// add that much junk to the hg repository. Instead, build a
// binary for the current system (only) and test that objdump
// can handle that one.

func testDisasm(t *testing.T, flags ...string) {
	tmp, exe := buildObjdump(t)
	defer os.RemoveAll(tmp)

	hello := filepath.Join(tmp, "hello.exe")
	args := []string{"build", "-o", hello}
	args = append(args, flags...)
	args = append(args, "testdata/fmthello.go")
	out, err := exec.Command("go", args...).CombinedOutput()
	if err != nil {
		t.Fatalf("go build fmthello.go: %v\n%s", err, out)
	}
	need := []string{
		"fmthello.go:6",
		"TEXT main.main(SB)",
	}
	switch runtime.GOARCH {
	case "amd64", "386":
		need = append(need, x86Need...)
	case "arm":
		need = append(need, armNeed...)
	}

	out, err = exec.Command(exe, "-s", "main.main", hello).CombinedOutput()
	if err != nil {
		t.Fatalf("objdump fmthello.exe: %v\n%s", err, out)
	}

	text := string(out)
	ok := true
	for _, s := range need {
		if !strings.Contains(text, s) {
			t.Errorf("disassembly missing '%s'", s)
			ok = false
		}
	}
	if !ok {
		t.Logf("full disassembly:\n%s", text)
	}
}

func TestDisasm(t *testing.T) {
	switch runtime.GOARCH {
	case "ppc64", "ppc64le":
		t.Skipf("skipping on %s, issue 9039", runtime.GOARCH)
	case "arm64":
		t.Skipf("skipping on %s, issue 10106", runtime.GOARCH)
	}
	testDisasm(t)
}

func TestDisasmExtld(t *testing.T) {
	switch runtime.GOOS {
	case "plan9", "windows":
		t.Skipf("skipping on %s", runtime.GOOS)
	}
	switch runtime.GOARCH {
	case "ppc64", "ppc64le":
		t.Skipf("skipping on %s, no support for external linking, issue 9038", runtime.GOARCH)
	case "arm64":
		t.Skipf("skipping on %s, issue 10106", runtime.GOARCH)
	}
	testDisasm(t, "-ldflags=-linkmode=external")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"gopkg.in/fsnotify.v1"

	"github.com/Felamande/god/lib/jsvm"

	"github.com/codegangsta/cli"
	"github.com/peterh/liner"

	"github.com/Felamande/god/lib/pathutil"
	_ "github.com/Felamande/god/modules/go"
	"github.com/Felamande/god/modules/god"
	"github.com/Felamande/god/modules/hotkey"
	_ "github.com/Felamande/god/modules/localstorage"

	_ "github.com/Felamande/god/modules/log"
	_ "github.com/Felamande/god/modules/os"
	_ "github.com/Felamande/god/modules/path"
)

var version = "2.0.0-beta3"

type vars map[string]string

//Cmder command manager
type Cmder struct {
	line           *liner.State
	history        *os.File
	w              *fsnotify.Watcher
	stopWChan      chan bool
	watchm         *sync.Mutex
	wd             string
	isStartWatch   bool
	global         vars
	varsReplacer   *strings.Replacer
	escapeReplacer *strings.Replacer
}

var cmder *Cmder

func init() {

	cmder = &Cmder{
		stopWChan: make(chan bool, 1),
		watchm:    new(sync.Mutex),
		global:    vars{"ps1": "(god) "},
		varsReplacer: strings.NewReplacer(
			"${wd}", pathutil.Wd(),
		),
		escapeReplacer: strings.NewReplacer(
			`\s`, " ",
			`\n`, "\n",
		),
	}

}
func main() {
	err := jsvm.Run("god.js")
	if err != nil {
		fmt.Println(err)
	}
	god.Init.Call()
	app := cli.NewApp()
	app.Version = version
	app.Name = filepath.Base(os.Args[0])
	app.HelpName = app.Name
	app.Usage = "An automation tool for Go project."
	app.Action = cmder.enterAction
	app.Commands = append(app.Commands, initCmd(), watchCmd(),
		reloadCmd(), exitCmd(), historyCmd(), unwatchCmd(), setCmd())

	for name, CbFunc := range god.SubCmd {
		app.Commands = append(app.Commands, newSubCmd(name, CbFunc))
	}

	err = hotkey.Bind("ctrl+d", func() { cmder.exit(nil) })
	if err != nil {
		fmt.Println(err)

	}
	err = hotkey.Bind("ctrl+alt+p", func() { cmder.unwatch(nil) })
	if err != nil {
		fmt.Println(err)
	}

	go hotkey.ApplyAll()

	app.Run(os.Args)
}

func (c *Cmder) enterAction(ctx *cli.Context) {
	line := liner.NewLiner()
	defer line.Close()
	line.SetCtrlCAborts(false)
	c.line = line

	historyFile, err := os.OpenFile(filepath.Join(os.TempDir(), ".god_hostory"), os.O_CREATE|os.O_RDWR, 0777)

	if err != nil {
		fmt.Println(err)

	}
	c.history = historyFile
	line.ReadHistory(historyFile)

	for {
		argStr, err := line.Prompt(c.global["ps1"])
		argStr = strings.TrimSpace(argStr)

		if len(argStr) == 0 {
			continue
		}
		line.AppendHistory(argStr)
		if err == liner.ErrPromptAborted {
			os.Exit(0)
		} else if err != nil {
			fmt.Println(err)
			continue
		}

		argStr = c.evalGlobal(argStr)
		splits := strings.Split(argStr, " ")
		var args = append([]string{}, "god")
		for _, s := range splits {
			if s != "" {
				args = append(args, s)
			}
		}
		if len(args) == 1 {
			continue
		}
		if ctx.App.Command(args[1]) == nil {
			continue
		}

		ctx.App.Run(args)

	}

}

func (c *Cmder) evalGlobal(raw string) string {
	return c.varsReplacer.Replace(raw)
}

func (c *Cmder) evalEscape(raw string) string {
	return c.escapeReplacer.Replace(raw)
}

func newSubCmd(name string, CbFunc jsvm.Func) cli.Command {
	return cli.Command{
		Name: name,
		Action: func(c *cli.Context) {
			arg := c.Args()
			flags, nargs, err := parseArgs(arg, "-")
			if err != nil {
				fmt.Println(name+": ", err)
				return
			}
			CbFunc.Call(nargs, flags)
		},
		Usage: "user defined command " + name,

		SkipFlagParsing: true,
	}
}

func (v vars) String() string {
	if len(v) == 0 {
		return ""
	}
	b, err := json.Marshal(v)
	if err != nil {
		return ""
	}
	return string(b)

}

func setCmd() cli.Command {
	return cli.Command{
		Name:      "set",
		Usage:     fmt.Sprintf("set <key> <value> defaults:%v\n", cmder.global),
		Action:    cmder.setfn,
		UsageText: `use \s to represent whitespace`,
	}
}

func (c *Cmder) setfn(ctx *cli.Context) {
	if len(ctx.Args()) != 2 {
		fmt.Println("set key value")
		return
	}
	k := ctx.Args()[0]
	v := ctx.Args()[1]
	c.global[k] = c.evalEscape(v)
}
<commit_msg>fix import<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"gopkg.in/fsnotify.v1"

	"github.com/Felamande/god/lib/jsvm"

	"github.com/codegangsta/cli"
	"github.com/peterh/liner"

	"github.com/Felamande/god/lib/pathutil"
	_ "github.com/Felamande/god/modules/go"
	"github.com/Felamande/god/modules/god"
	"github.com/Felamande/god/modules/hotkey"
	_ "github.com/Felamande/god/modules/localstorage"

	_ "github.com/Felamande/god/modules/log"
	_ "github.com/Felamande/god/modules/os"
	_ "github.com/Felamande/god/modules/path"
)

var version = "2.0.0-beta3"

type vars map[string]string

//Cmder command manager
type Cmder struct {
	line           *liner.State
	history        *os.File
	w              *fsnotify.Watcher
	stopWChan      chan bool
	watchm         *sync.Mutex
	wd             string
	isStartWatch   bool
	global         vars
	varsReplacer   *strings.Replacer
	escapeReplacer *strings.Replacer
}

var cmder *Cmder

func init() {

	cmder = &Cmder{
		stopWChan: make(chan bool, 1),
		watchm:    new(sync.Mutex),
		global:    vars{"ps1": "(god) "},
		varsReplacer: strings.NewReplacer(
			"${wd}", pathutil.Wd(),
		),
		escapeReplacer: strings.NewReplacer(
			`\s`, " ",
			`\n`, "\n",
		),
	}

}
func main() {
	err := jsvm.Run("god.js")
	if err != nil {
		fmt.Println(err)
	}

	god.Init.Call()
	app := cli.NewApp()
	app.Version = version
	app.Name = filepath.Base(os.Args[0])
	app.HelpName = app.Name
	app.Usage = "An automation tool for Go project."
	app.Action = cmder.enterAction
	app.Commands = append(app.Commands, initCmd(), watchCmd(),
		reloadCmd(), exitCmd(), historyCmd(), unwatchCmd(), setCmd())

	for name, CbFunc := range god.SubCmd {
		app.Commands = append(app.Commands, newSubCmd(name, CbFunc))
	}

	err = hotkey.Bind("ctrl+d", func() { cmder.exit(nil) })
	if err != nil {
		fmt.Println(err)

	}
	err = hotkey.Bind("ctrl+alt+p", func() { cmder.unwatch(nil) })
	if err != nil {
		fmt.Println(err)
	}

	go hotkey.ApplyAll()

	app.Run(os.Args)
}

func (c *Cmder) enterAction(ctx *cli.Context) {
	line := liner.NewLiner()
	defer line.Close()
	line.SetCtrlCAborts(false)
	c.line = line

	historyFile, err := os.OpenFile(filepath.Join(os.TempDir(), ".god_hostory"), os.O_CREATE|os.O_RDWR, 0777)

	if err != nil {
		fmt.Println(err)

	}
	c.history = historyFile
	line.ReadHistory(historyFile)

	for {
		argStr, err := line.Prompt(c.global["ps1"])
		argStr = strings.TrimSpace(argStr)

		if len(argStr) == 0 {
			continue
		}
		line.AppendHistory(argStr)
		if err == liner.ErrPromptAborted {
			os.Exit(0)
		} else if err != nil {
			fmt.Println(err)
			continue
		}

		argStr = c.evalGlobal(argStr)
		splits := strings.Split(argStr, " ")
		var args = append([]string{}, "god")
		for _, s := range splits {
			if s != "" {
				args = append(args, s)
			}
		}
		if len(args) == 1 {
			continue
		}
		if ctx.App.Command(args[1]) == nil {
			continue
		}

		ctx.App.Run(args)

	}

}

func (c *Cmder) evalGlobal(raw string) string {
	return c.varsReplacer.Replace(raw)
}

func (c *Cmder) evalEscape(raw string) string {
	return c.escapeReplacer.Replace(raw)
}

func newSubCmd(name string, CbFunc jsvm.Func) cli.Command {
	return cli.Command{
		Name: name,
		Action: func(c *cli.Context) {
			arg := c.Args()
			flags, nargs, err := parseArgs(arg, "-")
			if err != nil {
				fmt.Println(name+": ", err)
				return
			}
			CbFunc.Call(nargs, flags)
		},
		Usage: "user defined command " + name,

		SkipFlagParsing: true,
	}
}

func (v vars) String() string {
	if len(v) == 0 {
		return ""
	}
	b, err := json.Marshal(v)
	if err != nil {
		return ""
	}
	return string(b)

}

func setCmd() cli.Command {
	return cli.Command{
		Name:      "set",
		Usage:     fmt.Sprintf("set <key> <value> defaults:%v\n", cmder.global),
		Action:    cmder.setfn,
		UsageText: `use \s to represent whitespace`,
	}
}

func (c *Cmder) setfn(ctx *cli.Context) {
	if len(ctx.Args()) != 2 {
		fmt.Println("set key value")
		return
	}
	k := ctx.Args()[0]
	v := ctx.Args()[1]
	c.global[k] = c.evalEscape(v)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"go/build"
	"os"
	"path"
	"sort"
	"strings"

	"golang.org/x/tools/go/callgraph"
	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/pointer"
	"golang.org/x/tools/go/ssa"
	"golang.org/x/tools/go/ssa/ssautil"
)

var (
	ignoreFlag = flag.String("ignore", "",
		"ignore package path")

	limitFlag = flag.String("limit", "",
		"limit package path")

	focusFlag = flag.String("focus", "",
		"focus package name")

	subFlag = flag.String("sub", "",
		"subgraph by [type, pkg]")

	minlenFlag = flag.Uint("minlen", 2,
		"minlen of edge")

	testFlag = flag.Bool("test", false,
		"Loads test code (*_test.go) for imported packages")
)

func main() {
	flag.Parse()
	if err := doCallgraph(&build.Default, *focusFlag, *limitFlag, *ignoreFlag, *subFlag, *minlenFlag, *testFlag, flag.Args()); err != nil {
		fmt.Fprintf(os.Stderr, "go-callvis: %s\n", err)
		os.Exit(1)
	}
}

func doCallgraph(ctxt *build.Context, focusPkg, limitPath, ignorePath, subgraph string, minlen uint, tests bool, args []string) error {
	conf := loader.Config{Build: &build.Default}

	ignorePaths := []string{}
	for _, p := range strings.Split(ignorePath, ",") {
		if p != "" {
			ignorePaths = append(ignorePaths, p)
		}
	}

	if len(args) == 0 {
		fmt.Fprintln(os.Stderr, "missing arguments")
		return nil
	}
	_, err := conf.FromArgs(args, tests)
	if err != nil {
		return err
	}

	iprog, err := conf.Load()
	if err != nil {
		fmt.Print(err) // type error in some package
		return nil
	}

	prog := ssautil.CreateProgram(iprog, 0)
	prog.Build()

	main, err := mainPackage(prog, tests)
	if err != nil {
		return err
	}
	config := &pointer.Config{
		Mains:          []*ssa.Package{main},
		BuildCallGraph: true,
	}
	result, err := pointer.Analyze(config)
	if err != nil {
		return err // internal error in pointer analysis
	}
	result.CallGraph.DeleteSyntheticNodes()

	subType := subgraph == "type"
	subPkg := subgraph == "pkg"

	var edges []string
	edgeMap := make(map[string]struct{})
	callgraph.GraphVisitEdges(result.CallGraph, func(edge *callgraph.Edge) error {
		caller := edge.Caller.Func
		callee := edge.Callee.Func
		if caller.Pkg == nil || callee.Synthetic != "" {
			return nil
		}
		if !(caller.Pkg.Pkg.Name() == focusPkg || callee.Pkg.Pkg.Name() == focusPkg) {
			return nil
		}
		if strings.HasPrefix(caller.Pkg.Pkg.Path(), path.Join(main.Pkg.Path(), "vendor")) ||
			strings.HasPrefix(callee.Pkg.Pkg.Path(), path.Join(main.Pkg.Path(), "vendor")) {
			return nil
		}
		if !(strings.HasPrefix(caller.Pkg.Pkg.Path(), limitPath) &&
			strings.HasPrefix(callee.Pkg.Pkg.Path(), limitPath)) {
			return nil
		}
		for _, p := range ignorePaths {
			if strings.HasPrefix(caller.Pkg.Pkg.Path(), p) ||
				strings.HasPrefix(callee.Pkg.Pkg.Path(), p) {
				return nil
			}
		}

		callerProps := []string{}
		callerSign := caller.Signature
		if caller.Parent() != nil {
			callerSign = caller.Parent().Signature
		}
		callerLabel := fmt.Sprintf("%s\n%s", caller.Pkg.Pkg.Name(), caller.RelString(caller.Pkg.Pkg))
		if caller.Pkg.Pkg.Name() == focusPkg {
			callerProps = append(callerProps, "fillcolor=lightblue")
			callerLabel = fmt.Sprintf("%s", caller.RelString(caller.Pkg.Pkg))
			if subType && callerSign.Recv() != nil {
				callerParts := strings.Split(callerLabel, ".")
				callerLabel = callerParts[len(callerParts)-1]
			}
		} else if subPkg {
			callerLabel = fmt.Sprintf("%s", caller.RelString(caller.Pkg.Pkg))
		}
		callerProps = append(callerProps, fmt.Sprintf("label=%q", callerLabel))
		if caller.Parent() != nil {
			callerProps = append(callerProps, "style=\"dotted,rounded,filled\"")
		} else if caller.Object() != nil && caller.Object().Exported() {
			callerProps = append(callerProps, "style=\"bold,rounded,filled\"")
		}
		callerNode := fmt.Sprintf("%q [%s]", caller, strings.Join(callerProps, " "))
		if subType && caller.Pkg.Pkg.Name() == focusPkg && callerSign.Recv() != nil {
			parts := strings.Split(fmt.Sprint(callerSign.Recv().Type()), ".")
			clusterLabel := parts[len(parts)-1]
			callerNode = fmt.Sprintf("subgraph \"cluster_%s\" { penwidth=0.5; fontsize=18; label=\"%s\"; style=filled; fillcolor=snow; %s; }",
				callerSign.Recv().Type(), clusterLabel, callerNode)
		} else if subPkg && caller.Pkg.Pkg.Name() != focusPkg {
			callerNode = fmt.Sprintf("subgraph \"cluster_%s\" { penwidth=0.5; fontsize=18; label=\"%s\"; style=filled; fillcolor=snow; %s; }",
				caller.Pkg.Pkg.Name(), caller.Pkg.Pkg.Name(), callerNode)
		}

		calleeProps := []string{}
		calleeSign := callee.Signature
		if callee.Parent() != nil {
			calleeSign = callee.Parent().Signature
		}
		calleeLabel := fmt.Sprintf("%s\n%s", callee.Pkg.Pkg.Name(), callee.RelString(callee.Pkg.Pkg))
		if callee.Pkg.Pkg.Name() == focusPkg {
			calleeProps = append(calleeProps, "fillcolor=lightblue")
			calleeLabel = fmt.Sprintf("%s", callee.RelString(callee.Pkg.Pkg))
			if subType && calleeSign.Recv() != nil {
				calleeParts := strings.Split(calleeLabel, ".")
				calleeLabel = calleeParts[len(calleeParts)-1]
			}
		} else if subPkg {
			calleeLabel = fmt.Sprintf("%s", callee.RelString(callee.Pkg.Pkg))
		}
		calleeProps = append(calleeProps, fmt.Sprintf("label=%q", calleeLabel))
		if callee.Parent() != nil {
			calleeProps = append(calleeProps, "style=\"dotted,rounded,filled\"")
		} else if callee.Object() != nil && callee.Object().Exported() {
			calleeProps = append(calleeProps, "style=\"bold,rounded,filled\"")
		}
		calleeNode := fmt.Sprintf("%q [%s]", callee, strings.Join(calleeProps, " "))
		if subType && callee.Pkg.Pkg.Name() == focusPkg && calleeSign.Recv() != nil {
			parts := strings.Split(fmt.Sprint(calleeSign.Recv().Type()), ".")
			clusterLabel := parts[len(parts)-1]
			calleeNode = fmt.Sprintf("subgraph \"cluster_%s\" { penwidth=0.5; fontsize=18; label=\"%s\"; style=filled; fillcolor=snow; %s; }",
				calleeSign.Recv().Type(), clusterLabel, calleeNode)
		} else if subPkg && callee.Pkg.Pkg.Name() != focusPkg {
			calleeNode = fmt.Sprintf("subgraph \"cluster_%s\" { penwidth=0.5; fontsize=18; label=\"%s\"; style=filled; fillcolor=snow; %s; }",
				callee.Pkg.Pkg.Name(), callee.Pkg.Pkg.Name(), calleeNode)
		}

		edgeProps := []string{}
		if edge.Site != nil && edge.Site.Common().StaticCallee() == nil {
			edgeProps = append(edgeProps, "style=dashed")
		}
		switch edge.Site.(type) {
		case *ssa.Go:
			edgeProps = append(edgeProps, "arrowhead=empty")
		case *ssa.Defer:
			edgeProps = append(edgeProps, "arrowhead=odot")
		}
		if callee.Pkg.Pkg.Name() != focusPkg || caller.Pkg.Pkg.Name() != focusPkg {
			edgeProps = append(edgeProps, "color=saddlebrown")
		}
		s := fmt.Sprintf("%s;%s; %q -> %q [%s]",
			callerNode, calleeNode,
			caller, callee, strings.Join(edgeProps, " "))
		if _, ok := edgeMap[s]; !ok {
			edges = append(edges, s)
			edgeMap[s] = struct{}{}
		}

		return nil
	})

	sort.Strings(edges)

	fmt.Printf(`digraph G {
        label="%s";
        labelloc=t;
        bgcolor=aliceblue;
        rankdir=LR;
        fontsize=22;
        fontname="Ubuntu";
        edge [minlen=%d];
        node [shape=box style="rounded,filled" fillcolor=wheat fontname="Ubuntu"];
`, focusPkg, minlen)
	for _, edge := range edges {
		fmt.Println("\t", edge)
	}
	fmt.Println("}")

	//fmt.Println(len(edges), "edges")
	return nil
}

func mainPackage(prog *ssa.Program, tests bool) (*ssa.Package, error) {
	pkgs := prog.AllPackages()
	/*if tests {
		if len(pkgs) > 0 {
			if main := prog.CreateTestMainPackage(pkgs...); main != nil {
				return main, nil
			}
		}
		return nil, fmt.Errorf("no tests")
	}*/
	for _, pkg := range pkgs {
		if pkg.Pkg.Name() == "main" {
			if pkg.Func("main") == nil {
				return nil, fmt.Errorf("no func main() in main package")
			}
			return pkg, nil
		}
	}
	return nil, fmt.Errorf("no main package")
}
<commit_msg>Focus 'main' package as default<commit_after>package main

import (
	"flag"
	"fmt"
	"go/build"
	"os"
	"path"
	"sort"
	"strings"

	"golang.org/x/tools/go/callgraph"
	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/pointer"
	"golang.org/x/tools/go/ssa"
	"golang.org/x/tools/go/ssa/ssautil"
)

var (
	ignoreFlag = flag.String("ignore", "",
		"ignore package path")

	limitFlag = flag.String("limit", "",
		"limit package path")

	focusFlag = flag.String("focus", "main",
		"focus package name")

	subFlag = flag.String("sub", "",
		"subgraph by [type, pkg]")

	minlenFlag = flag.Uint("minlen", 2,
		"minlen of edge")

	testFlag = flag.Bool("test", false,
		"Loads test code (*_test.go) for imported packages")
)

func main() {
	flag.Parse()
	if err := doCallgraph(&build.Default, *focusFlag, *limitFlag, *ignoreFlag, *subFlag, *minlenFlag, *testFlag, flag.Args()); err != nil {
		fmt.Fprintf(os.Stderr, "go-callvis: %s\n", err)
		os.Exit(1)
	}
}

func doCallgraph(ctxt *build.Context, focusPkg, limitPath, ignorePath, subgraph string, minlen uint, tests bool, args []string) error {
	conf := loader.Config{Build: &build.Default}

	ignorePaths := []string{}
	for _, p := range strings.Split(ignorePath, ",") {
		if p != "" {
			ignorePaths = append(ignorePaths, p)
		}
	}

	if len(args) == 0 {
		fmt.Fprintln(os.Stderr, "missing arguments")
		return nil
	}
	_, err := conf.FromArgs(args, tests)
	if err != nil {
		return err
	}

	iprog, err := conf.Load()
	if err != nil {
		fmt.Print(err) // type error in some package
		return nil
	}

	prog := ssautil.CreateProgram(iprog, 0)
	prog.Build()

	main, err := mainPackage(prog, tests)
	if err != nil {
		return err
	}
	config := &pointer.Config{
		Mains:          []*ssa.Package{main},
		BuildCallGraph: true,
	}
	result, err := pointer.Analyze(config)
	if err != nil {
		return err // internal error in pointer analysis
	}
	result.CallGraph.DeleteSyntheticNodes()

	subType := subgraph == "type"
	subPkg := subgraph == "pkg"

	var edges []string
	edgeMap := make(map[string]struct{})
	callgraph.GraphVisitEdges(result.CallGraph, func(edge *callgraph.Edge) error {
		caller := edge.Caller.Func
		callee := edge.Callee.Func
		if caller.Pkg == nil || callee.Synthetic != "" {
			return nil
		}
		if !(caller.Pkg.Pkg.Name() == focusPkg || callee.Pkg.Pkg.Name() == focusPkg) {
			return nil
		}
		if strings.HasPrefix(caller.Pkg.Pkg.Path(), path.Join(main.Pkg.Path(), "vendor")) ||
			strings.HasPrefix(callee.Pkg.Pkg.Path(), path.Join(main.Pkg.Path(), "vendor")) {
			return nil
		}
		if !(strings.HasPrefix(caller.Pkg.Pkg.Path(), limitPath) &&
			strings.HasPrefix(callee.Pkg.Pkg.Path(), limitPath)) {
			return nil
		}
		for _, p := range ignorePaths {
			if strings.HasPrefix(caller.Pkg.Pkg.Path(), p) ||
				strings.HasPrefix(callee.Pkg.Pkg.Path(), p) {
				return nil
			}
		}

		callerProps := []string{}
		callerSign := caller.Signature
		if caller.Parent() != nil {
			callerSign = caller.Parent().Signature
		}
		callerLabel := fmt.Sprintf("%s\n%s", caller.Pkg.Pkg.Name(), caller.RelString(caller.Pkg.Pkg))
		if caller.Pkg.Pkg.Name() == focusPkg {
			callerProps = append(callerProps, "fillcolor=lightblue")
			callerLabel = fmt.Sprintf("%s", caller.RelString(caller.Pkg.Pkg))
			if subType && callerSign.Recv() != nil {
				callerParts := strings.Split(callerLabel, ".")
				callerLabel = callerParts[len(callerParts)-1]
			}
		} else if subPkg {
			callerLabel = fmt.Sprintf("%s", caller.RelString(caller.Pkg.Pkg))
		}
		callerProps = append(callerProps, fmt.Sprintf("label=%q", callerLabel))
		if caller.Parent() != nil {
			callerProps = append(callerProps, "style=\"dotted,rounded,filled\"")
		} else if caller.Object() != nil && caller.Object().Exported() {
			callerProps = append(callerProps, "style=\"bold,rounded,filled\"")
		}
		callerNode := fmt.Sprintf("%q [%s]", caller, strings.Join(callerProps, " "))
		if subType && caller.Pkg.Pkg.Name() == focusPkg && callerSign.Recv() != nil {
			parts := strings.Split(fmt.Sprint(callerSign.Recv().Type()), ".")
			clusterLabel := parts[len(parts)-1]
			callerNode = fmt.Sprintf("subgraph \"cluster_%s\" { penwidth=0.5; fontsize=18; label=\"%s\"; style=filled; fillcolor=snow; %s; }",
				callerSign.Recv().Type(), clusterLabel, callerNode)
		} else if subPkg && caller.Pkg.Pkg.Name() != focusPkg {
			callerNode = fmt.Sprintf("subgraph \"cluster_%s\" { penwidth=0.5; fontsize=18; label=\"%s\"; style=filled; fillcolor=snow; %s; }",
				caller.Pkg.Pkg.Name(), caller.Pkg.Pkg.Name(), callerNode)
		}

		calleeProps := []string{}
		calleeSign := callee.Signature
		if callee.Parent() != nil {
			calleeSign = callee.Parent().Signature
		}
		calleeLabel := fmt.Sprintf("%s\n%s", callee.Pkg.Pkg.Name(), callee.RelString(callee.Pkg.Pkg))
		if callee.Pkg.Pkg.Name() == focusPkg {
			calleeProps = append(calleeProps, "fillcolor=lightblue")
			calleeLabel = fmt.Sprintf("%s", callee.RelString(callee.Pkg.Pkg))
			if subType && calleeSign.Recv() != nil {
				calleeParts := strings.Split(calleeLabel, ".")
				calleeLabel = calleeParts[len(calleeParts)-1]
			}
		} else if subPkg {
			calleeLabel = fmt.Sprintf("%s", callee.RelString(callee.Pkg.Pkg))
		}
		calleeProps = append(calleeProps, fmt.Sprintf("label=%q", calleeLabel))
		if callee.Parent() != nil {
			calleeProps = append(calleeProps, "style=\"dotted,rounded,filled\"")
		} else if callee.Object() != nil && callee.Object().Exported() {
			calleeProps = append(calleeProps, "style=\"bold,rounded,filled\"")
		}
		calleeNode := fmt.Sprintf("%q [%s]", callee, strings.Join(calleeProps, " "))
		if subType && callee.Pkg.Pkg.Name() == focusPkg && calleeSign.Recv() != nil {
			parts := strings.Split(fmt.Sprint(calleeSign.Recv().Type()), ".")
			clusterLabel := parts[len(parts)-1]
			calleeNode = fmt.Sprintf("subgraph \"cluster_%s\" { penwidth=0.5; fontsize=18; label=\"%s\"; style=filled; fillcolor=snow; %s; }",
				calleeSign.Recv().Type(), clusterLabel, calleeNode)
		} else if subPkg && callee.Pkg.Pkg.Name() != focusPkg {
			calleeNode = fmt.Sprintf("subgraph \"cluster_%s\" { penwidth=0.5; fontsize=18; label=\"%s\"; style=filled; fillcolor=snow; %s; }",
				callee.Pkg.Pkg.Name(), callee.Pkg.Pkg.Name(), calleeNode)
		}

		edgeProps := []string{}
		if edge.Site != nil && edge.Site.Common().StaticCallee() == nil {
			edgeProps = append(edgeProps, "style=dashed")
		}
		switch edge.Site.(type) {
		case *ssa.Go:
			edgeProps = append(edgeProps, "arrowhead=empty")
		case *ssa.Defer:
			edgeProps = append(edgeProps, "arrowhead=odot")
		}
		if callee.Pkg.Pkg.Name() != focusPkg || caller.Pkg.Pkg.Name() != focusPkg {
			edgeProps = append(edgeProps, "color=saddlebrown")
		}
		s := fmt.Sprintf("%s;%s; %q -> %q [%s]",
			callerNode, calleeNode,
			caller, callee, strings.Join(edgeProps, " "))
		if _, ok := edgeMap[s]; !ok {
			edges = append(edges, s)
			edgeMap[s] = struct{}{}
		}

		return nil
	})

	sort.Strings(edges)

	fmt.Printf(`digraph G {
        label="%s";
        labelloc=t;
        bgcolor=aliceblue;
        rankdir=LR;
        fontsize=22;
        fontname="Ubuntu";
        edge [minlen=%d];
        node [shape=box style="rounded,filled" fillcolor=wheat fontname="Ubuntu"];
`, focusPkg, minlen)
	for _, edge := range edges {
		fmt.Println("\t", edge)
	}
	fmt.Println("}")

	//fmt.Println(len(edges), "edges")
	return nil
}

func mainPackage(prog *ssa.Program, tests bool) (*ssa.Package, error) {
	pkgs := prog.AllPackages()
	/*if tests {
		if len(pkgs) > 0 {
			if main := prog.CreateTestMainPackage(pkgs...); main != nil {
				return main, nil
			}
		}
		return nil, fmt.Errorf("no tests")
	}*/
	for _, pkg := range pkgs {
		if pkg.Pkg.Name() == "main" {
			if pkg.Func("main") == nil {
				return nil, fmt.Errorf("no func main() in main package")
			}
			return pkg, nil
		}
	}
	return nil, fmt.Errorf("no main package")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"time"

	"github.com/alecthomas/kingpin"
	"github.com/apex/log"
	"github.com/apex/log/handlers/cli"
	"github.com/caarlos0/ctrlc"
	"github.com/fatih/color"
	"github.com/goreleaser/goreleaser/internal/pipe"
	"github.com/goreleaser/goreleaser/internal/pipeline"
	"github.com/goreleaser/goreleaser/pkg/config"
	"github.com/goreleaser/goreleaser/pkg/context"
)

var (
	version = "dev"
	commit  = "none"
	date    = "unknown"
)

type releaseOptions struct {
	Config       string
	ReleaseNotes string
	Snapshot     bool
	SkipPublish  bool
	SkipSign     bool
	SkipValidate bool
	RmDist       bool
	Debug        bool
	Parallelism  int
	Timeout      time.Duration
}

func init() {
	// enable colored output on travis
	if os.Getenv("CI") == "Travis" {
		color.NoColor = false
	}
	log.SetHandler(cli.Default)
}

func main() {
	fmt.Println()
	defer fmt.Println()

	var app = kingpin.New("goreleaser", "Deliver Go binaries as fast and easily as possible")
	var initCmd = app.Command("init", "Generates a .goreleaser.yml file").Alias("i")
	var releaseCmd = app.Command("release", "Releases the current project").Alias("r").Default()
	var config = releaseCmd.Flag("config", "Load configuration from file").Short('c').Short('f').PlaceHolder(".goreleaser.yml").String()
	var releaseNotes = releaseCmd.Flag("release-notes", "Load custom release notes from a markdown file").PlaceHolder("notes.md").String()
	var snapshot = releaseCmd.Flag("snapshot", "Generate an unversioned snapshot release, skipping all validations and without publishing any artifacts").Bool()
	var skipPublish = releaseCmd.Flag("skip-publish", "Generates all artifacts but does not publish them anywhere").Bool()
	var skipSign = releaseCmd.Flag("skip-sign", "Skips signing the artifacts").Bool()
	var skipValidate = releaseCmd.Flag("skip-validate", "Skips all git sanity checks").Bool()
	var rmDist = releaseCmd.Flag("rm-dist", "Remove the dist folder before building").Bool()
	var parallelism = releaseCmd.Flag("parallelism", "Amount of slow tasks to do in concurrently").Short('p').Default("4").Int() // TODO: use runtime.NumCPU here?
	var debug = releaseCmd.Flag("debug", "Enable debug mode").Bool()
	var timeout = releaseCmd.Flag("timeout", "Timeout to the entire release process").Default("30m").Duration()

	app.Version(fmt.Sprintf("%v, commit %v, built at %v", version, commit, date))
	app.VersionFlag.Short('v')
	app.HelpFlag.Short('h')

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case initCmd.FullCommand():
		var filename = ".goreleaser.yml"
		if err := initProject(filename); err != nil {
			log.WithError(err).Error("failed to init project")
			terminate(1)
			return
		}
		log.WithField("file", filename).Info("config created; please edit accordingly to your needs")
	case releaseCmd.FullCommand():
		start := time.Now()
		log.Infof(color.New(color.Bold).Sprintf("releasing using goreleaser %s...", version))
		var options = releaseOptions{
			Config:       *config,
			ReleaseNotes: *releaseNotes,
			Snapshot:     *snapshot,
			SkipPublish:  *skipPublish,
			SkipValidate: *skipValidate,
			SkipSign:     *skipSign,
			RmDist:       *rmDist,
			Parallelism:  *parallelism,
			Debug:        *debug,
			Timeout:      *timeout,
		}
		if err := releaseProject(options); err != nil {
			log.WithError(err).Errorf(color.New(color.Bold).Sprintf("release failed after %0.2fs", time.Since(start).Seconds()))
			terminate(1)
			return
		}
		log.Infof(color.New(color.Bold).Sprintf("release succeeded after %0.2fs", time.Since(start).Seconds()))
	}
}

func terminate(status int) {
	os.Exit(status)
}

func releaseProject(options releaseOptions) error {
	if options.Debug {
		log.SetLevel(log.DebugLevel)
	}
	cfg, err := loadConfig(options.Config)
	if err != nil {
		return err
	}
	ctx, cancel := context.NewWithTimeout(cfg, options.Timeout)
	defer cancel()
	ctx.Parallelism = options.Parallelism
	ctx.Debug = options.Debug
	log.Debugf("parallelism: %v", ctx.Parallelism)
	if options.ReleaseNotes != "" {
		bts, err := ioutil.ReadFile(options.ReleaseNotes)
		if err != nil {
			return err
		}
		log.WithField("file", options.ReleaseNotes).Info("loaded custom release notes")
		log.WithField("file", options.ReleaseNotes).Debugf("custom release notes: \n%s", string(bts))
		ctx.ReleaseNotes = string(bts)
	}
	ctx.Snapshot = options.Snapshot
	ctx.SkipPublish = ctx.Snapshot || options.SkipPublish
	ctx.SkipValidate = ctx.Snapshot || options.SkipValidate
	ctx.SkipSign = options.SkipSign
	ctx.RmDist = options.RmDist
	return doRelease(ctx)
}

var bold = color.New(color.Bold)

func doRelease(ctx *context.Context) error {
	defer func() { cli.Default.Padding = 3 }()
	var release = func() error {
		for _, pipe := range pipeline.Pipeline {
			cli.Default.Padding = 3
			log.Infof(bold.Sprint(strings.ToUpper(pipe.String())))
			cli.Default.Padding = 6
			if err := handle(pipe.Run(ctx)); err != nil {
				return err
			}
		}
		return nil
	}
	return ctrlc.Default.Run(ctx, release)
}

func handle(err error) error {
	if err == nil {
		return nil
	}
	if pipe.IsSkip(err) {
		log.WithField("reason", err.Error()).Warn("skipped")
		return nil
	}
	return err
}

// InitProject creates an example goreleaser.yml in the current directory
func initProject(filename string) error {
	if _, err := os.Stat(filename); !os.IsNotExist(err) {
		if err != nil {
			return err
		}
		return fmt.Errorf("%s already exists", filename)
	}
	log.Infof(color.New(color.Bold).Sprintf("Generating %s file", filename))
	return ioutil.WriteFile(filename, []byte(exampleConfig), 0644)
}

func loadConfig(path string) (config.Project, error) {
	if path != "" {
		return config.Load(path)
	}
	for _, f := range [4]string{
		".goreleaser.yml",
		".goreleaser.yaml",
		"goreleaser.yml",
		"goreleaser.yaml",
	} {
		proj, err := config.Load(f)
		if err != nil && os.IsNotExist(err) {
			continue
		}
		return proj, err
	}
	// the user didn't specified a config file and the known files
	// doest not exist, so, return an empty config and a nil err.
	log.Warn("could not load config, using defaults")
	return config.Project{}, nil
}

var exampleConfig = `# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
before:
  hooks:
    # you may remove this if you don't use vgo
    - go mod download
    # you may remove this if you don't need go generate
    - go generate ./...
builds:
- env:
  - CGO_ENABLED=0
archive:
  replacements:
    darwin: Darwin
    linux: Linux
    windows: Windows
    386: i386
    amd64: x86_64
checksum:
  name_template: 'checksums.txt'
snapshot:
  name_template: "{{ .Tag }}-next"
changelog:
  sort: asc
  filters:
    exclude:
    - '^docs:'
    - '^test:'
`
<commit_msg>fix: CI is not Travis<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"time"

	"github.com/alecthomas/kingpin"
	"github.com/apex/log"
	"github.com/apex/log/handlers/cli"
	"github.com/caarlos0/ctrlc"
	"github.com/fatih/color"
	"github.com/goreleaser/goreleaser/internal/pipe"
	"github.com/goreleaser/goreleaser/internal/pipeline"
	"github.com/goreleaser/goreleaser/pkg/config"
	"github.com/goreleaser/goreleaser/pkg/context"
)

var (
	version = "dev"
	commit  = "none"
	date    = "unknown"
)

type releaseOptions struct {
	Config       string
	ReleaseNotes string
	Snapshot     bool
	SkipPublish  bool
	SkipSign     bool
	SkipValidate bool
	RmDist       bool
	Debug        bool
	Parallelism  int
	Timeout      time.Duration
}

func init() {
	// enable colored output on travis
	if os.Getenv("CI") != "" {
		color.NoColor = false
	}
	log.SetHandler(cli.Default)
}

func main() {
	fmt.Println()
	defer fmt.Println()

	var app = kingpin.New("goreleaser", "Deliver Go binaries as fast and easily as possible")
	var initCmd = app.Command("init", "Generates a .goreleaser.yml file").Alias("i")
	var releaseCmd = app.Command("release", "Releases the current project").Alias("r").Default()
	var config = releaseCmd.Flag("config", "Load configuration from file").Short('c').Short('f').PlaceHolder(".goreleaser.yml").String()
	var releaseNotes = releaseCmd.Flag("release-notes", "Load custom release notes from a markdown file").PlaceHolder("notes.md").String()
	var snapshot = releaseCmd.Flag("snapshot", "Generate an unversioned snapshot release, skipping all validations and without publishing any artifacts").Bool()
	var skipPublish = releaseCmd.Flag("skip-publish", "Generates all artifacts but does not publish them anywhere").Bool()
	var skipSign = releaseCmd.Flag("skip-sign", "Skips signing the artifacts").Bool()
	var skipValidate = releaseCmd.Flag("skip-validate", "Skips all git sanity checks").Bool()
	var rmDist = releaseCmd.Flag("rm-dist", "Remove the dist folder before building").Bool()
	var parallelism = releaseCmd.Flag("parallelism", "Amount of slow tasks to do in concurrently").Short('p').Default("4").Int() // TODO: use runtime.NumCPU here?
	var debug = releaseCmd.Flag("debug", "Enable debug mode").Bool()
	var timeout = releaseCmd.Flag("timeout", "Timeout to the entire release process").Default("30m").Duration()

	app.Version(fmt.Sprintf("%v, commit %v, built at %v", version, commit, date))
	app.VersionFlag.Short('v')
	app.HelpFlag.Short('h')

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case initCmd.FullCommand():
		var filename = ".goreleaser.yml"
		if err := initProject(filename); err != nil {
			log.WithError(err).Error("failed to init project")
			terminate(1)
			return
		}
		log.WithField("file", filename).Info("config created; please edit accordingly to your needs")
	case releaseCmd.FullCommand():
		start := time.Now()
		log.Infof(color.New(color.Bold).Sprintf("releasing using goreleaser %s...", version))
		var options = releaseOptions{
			Config:       *config,
			ReleaseNotes: *releaseNotes,
			Snapshot:     *snapshot,
			SkipPublish:  *skipPublish,
			SkipValidate: *skipValidate,
			SkipSign:     *skipSign,
			RmDist:       *rmDist,
			Parallelism:  *parallelism,
			Debug:        *debug,
			Timeout:      *timeout,
		}
		if err := releaseProject(options); err != nil {
			log.WithError(err).Errorf(color.New(color.Bold).Sprintf("release failed after %0.2fs", time.Since(start).Seconds()))
			terminate(1)
			return
		}
		log.Infof(color.New(color.Bold).Sprintf("release succeeded after %0.2fs", time.Since(start).Seconds()))
	}
}

func terminate(status int) {
	os.Exit(status)
}

func releaseProject(options releaseOptions) error {
	if options.Debug {
		log.SetLevel(log.DebugLevel)
	}
	cfg, err := loadConfig(options.Config)
	if err != nil {
		return err
	}
	ctx, cancel := context.NewWithTimeout(cfg, options.Timeout)
	defer cancel()
	ctx.Parallelism = options.Parallelism
	ctx.Debug = options.Debug
	log.Debugf("parallelism: %v", ctx.Parallelism)
	if options.ReleaseNotes != "" {
		bts, err := ioutil.ReadFile(options.ReleaseNotes)
		if err != nil {
			return err
		}
		log.WithField("file", options.ReleaseNotes).Info("loaded custom release notes")
		log.WithField("file", options.ReleaseNotes).Debugf("custom release notes: \n%s", string(bts))
		ctx.ReleaseNotes = string(bts)
	}
	ctx.Snapshot = options.Snapshot
	ctx.SkipPublish = ctx.Snapshot || options.SkipPublish
	ctx.SkipValidate = ctx.Snapshot || options.SkipValidate
	ctx.SkipSign = options.SkipSign
	ctx.RmDist = options.RmDist
	return doRelease(ctx)
}

var bold = color.New(color.Bold)

func doRelease(ctx *context.Context) error {
	defer func() { cli.Default.Padding = 3 }()
	var release = func() error {
		for _, pipe := range pipeline.Pipeline {
			cli.Default.Padding = 3
			log.Infof(bold.Sprint(strings.ToUpper(pipe.String())))
			cli.Default.Padding = 6
			if err := handle(pipe.Run(ctx)); err != nil {
				return err
			}
		}
		return nil
	}
	return ctrlc.Default.Run(ctx, release)
}

func handle(err error) error {
	if err == nil {
		return nil
	}
	if pipe.IsSkip(err) {
		log.WithField("reason", err.Error()).Warn("skipped")
		return nil
	}
	return err
}

// InitProject creates an example goreleaser.yml in the current directory
func initProject(filename string) error {
	if _, err := os.Stat(filename); !os.IsNotExist(err) {
		if err != nil {
			return err
		}
		return fmt.Errorf("%s already exists", filename)
	}
	log.Infof(color.New(color.Bold).Sprintf("Generating %s file", filename))
	return ioutil.WriteFile(filename, []byte(exampleConfig), 0644)
}

func loadConfig(path string) (config.Project, error) {
	if path != "" {
		return config.Load(path)
	}
	for _, f := range [4]string{
		".goreleaser.yml",
		".goreleaser.yaml",
		"goreleaser.yml",
		"goreleaser.yaml",
	} {
		proj, err := config.Load(f)
		if err != nil && os.IsNotExist(err) {
			continue
		}
		return proj, err
	}
	// the user didn't specified a config file and the known files
	// doest not exist, so, return an empty config and a nil err.
	log.Warn("could not load config, using defaults")
	return config.Project{}, nil
}

var exampleConfig = `# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
before:
  hooks:
    # you may remove this if you don't use vgo
    - go mod download
    # you may remove this if you don't need go generate
    - go generate ./...
builds:
- env:
  - CGO_ENABLED=0
archive:
  replacements:
    darwin: Darwin
    linux: Linux
    windows: Windows
    386: i386
    amd64: x86_64
checksum:
  name_template: 'checksums.txt'
snapshot:
  name_template: "{{ .Tag }}-next"
changelog:
  sort: asc
  filters:
    exclude:
    - '^docs:'
    - '^test:'
`
<|endoftext|>


<commit_before>package main

import (
	"context"
	"html/template"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"regexp"
	"sync"
	"syscall"

	"github.com/gorilla/websocket"
	"github.com/hpcloud/tail"
	maxminddb "github.com/oschwald/maxminddb-golang"
)

type broadcaster struct {
	mu sync.Mutex
	cs []chan<- interface{}
}

func (b *broadcaster) sub(c chan<- interface{}) {
	b.mu.Lock()
	defer b.mu.Unlock()

	b.cs = append(b.cs, c)
}

func (b *broadcaster) usub(c chan<- interface{}) {
	b.mu.Lock()
	defer b.mu.Unlock()

	n := b.cs[:0]
	for _, x := range b.cs {
		if x != c {
			n = append(n, x)
		}
	}
	b.cs = n
}

func (b *broadcaster) pub(v interface{}) {
	b.mu.Lock()
	defer b.mu.Unlock()

	for _, c := range b.cs {
		c <- v
	}
}

func (b *broadcaster) close() {
	b.mu.Lock()
	defer b.mu.Unlock()

	for _, c := range b.cs {
		close(c)
	}
}

type mmrecord struct {
	Location struct {
		Latitude  float64 `maxminddb:"latitude"`
		Longitude float64 `maxminddb:"longitude"`
	} `maxminddb:"location"`
}

var b *broadcaster

func main() {
	logFilename := os.Getenv("LOG_FILENAME")
	if logFilename == "" {
		log.Fatal("LOG_FILENAME env variable is required")
	}

	gdb, err := maxminddb.Open("GeoLite2-City.mmdb")
	if err != nil {
		log.Fatalf("failed to open maxmind db: %v", err)
	}
	defer gdb.Close()

	t, err := tail.TailFile(logFilename, tail.Config{
		Follow:   true,
		Location: &tail.SeekInfo{Whence: os.SEEK_END},
	})
	if err != nil {
		log.Fatalf("failed to tail %s: %v", "ips.log", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/ws", ws)
	mux.HandleFunc("/", index)
	httpListenOn := os.Getenv("HTTP_LISTEN_ON")
	if httpListenOn == "" {
		httpListenOn = ":8080"
	}
	server := http.Server{Addr: httpListenOn, Handler: mux}

	wg := sync.WaitGroup{}
	go func() {
		wg.Add(1)
		defer wg.Done()

		if err := server.ListenAndServe(); err != nil {
			log.Println(err)
		}
	}()

	b = &broadcaster{}

	go func() {
		wg.Add(1)
		defer wg.Done()

		ipregexp := regexp.MustCompile(`\d+\.\d+\.\d+\.\d+`)

		for line := range t.Lines {
			ipstr := ipregexp.FindString(line.Text)
			if ipstr == "" {
				log.Printf("failed to find IP addres in: %s", line.Text)
				continue
			}
			ip := net.ParseIP(ipstr)

			res := mmrecord{}
			if err := gdb.Lookup(ip, &res); err != nil {
				log.Printf("failed to lookup ip %s location: %v", ipstr, err)
				continue
			}
			b.pub([]float64{res.Location.Latitude, res.Location.Longitude})
		}
	}()

	sigs := make(chan os.Signal)
	signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)
	<-sigs
	log.Println("interrupted, shutting down the server")
	if err := t.Stop(); err != nil {
		log.Println(err)
	}
	if err := server.Shutdown(context.Background()); err != nil {
		log.Println(err)
	}
	wg.Wait()
}

func index(w http.ResponseWriter, r *http.Request) {
	tmpl, err := template.ParseFiles("index.html")
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	data := struct{ Host string }{Host: r.Host}
	if err := tmpl.Execute(w, data); err != nil {
		http.Error(w, err.Error(), 500)
	}
}

var upgrader = websocket.Upgrader{}

func ws(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	defer conn.Close()

	c := make(chan interface{})
	b.sub(c)
	defer b.usub(c)

	for v := range c {
		if err := conn.WriteJSON(v); err != nil {
			log.Println(err)
			break
		}
	}
}
<commit_msg>Log messages upon goroutines exiting<commit_after>package main

import (
	"context"
	"html/template"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"regexp"
	"sync"
	"syscall"

	"github.com/gorilla/websocket"
	"github.com/hpcloud/tail"
	maxminddb "github.com/oschwald/maxminddb-golang"
)

type broadcaster struct {
	mu sync.Mutex
	cs []chan<- interface{}
}

func (b *broadcaster) sub(c chan<- interface{}) {
	b.mu.Lock()
	defer b.mu.Unlock()

	b.cs = append(b.cs, c)
}

func (b *broadcaster) usub(c chan<- interface{}) {
	b.mu.Lock()
	defer b.mu.Unlock()

	n := b.cs[:0]
	for _, x := range b.cs {
		if x != c {
			n = append(n, x)
		}
	}
	b.cs = n
}

func (b *broadcaster) pub(v interface{}) {
	b.mu.Lock()
	defer b.mu.Unlock()

	for _, c := range b.cs {
		c <- v
	}
}

func (b *broadcaster) close() {
	b.mu.Lock()
	defer b.mu.Unlock()

	for _, c := range b.cs {
		close(c)
	}
}

type mmrecord struct {
	Location struct {
		Latitude  float64 `maxminddb:"latitude"`
		Longitude float64 `maxminddb:"longitude"`
	} `maxminddb:"location"`
}

var b *broadcaster

func main() {
	logFilename := os.Getenv("LOG_FILENAME")
	if logFilename == "" {
		log.Fatal("LOG_FILENAME env variable is required")
	}

	gdb, err := maxminddb.Open("GeoLite2-City.mmdb")
	if err != nil {
		log.Fatalf("failed to open maxmind db: %v", err)
	}
	defer gdb.Close()

	t, err := tail.TailFile(logFilename, tail.Config{
		Follow:   true,
		Location: &tail.SeekInfo{Whence: os.SEEK_END},
	})
	if err != nil {
		log.Fatalf("failed to tail %s: %v", "ips.log", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/ws", ws)
	mux.HandleFunc("/", index)
	httpListenOn := os.Getenv("HTTP_LISTEN_ON")
	if httpListenOn == "" {
		httpListenOn = ":8080"
	}
	server := http.Server{Addr: httpListenOn, Handler: mux}

	wg := sync.WaitGroup{}
	go func() {
		wg.Add(1)
		defer wg.Done()

		if err := server.ListenAndServe(); err != nil {
			log.Println(err)
		}
		log.Println("server goroutine has exited")
	}()

	b = &broadcaster{}

	go func() {
		wg.Add(1)
		defer wg.Done()

		ipregexp := regexp.MustCompile(`\d+\.\d+\.\d+\.\d+`)

		for line := range t.Lines {
			ipstr := ipregexp.FindString(line.Text)
			if ipstr == "" {
				log.Printf("failed to find IP addres in: %s", line.Text)
				continue
			}
			ip := net.ParseIP(ipstr)

			res := mmrecord{}
			if err := gdb.Lookup(ip, &res); err != nil {
				log.Printf("failed to lookup ip %s location: %v", ipstr, err)
				continue
			}
			b.pub([]float64{res.Location.Latitude, res.Location.Longitude})
		}
		log.Println("log tailing goroutine has exited")
	}()

	sigs := make(chan os.Signal)
	signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)
	<-sigs
	log.Println("interrupted, shutting down the server")
	if err := t.Stop(); err != nil {
		log.Println(err)
	}
	if err := server.Shutdown(context.Background()); err != nil {
		log.Println(err)
	}
	wg.Wait()
}

func index(w http.ResponseWriter, r *http.Request) {
	tmpl, err := template.ParseFiles("index.html")
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	data := struct{ Host string }{Host: r.Host}
	if err := tmpl.Execute(w, data); err != nil {
		http.Error(w, err.Error(), 500)
	}
}

var upgrader = websocket.Upgrader{}

func ws(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	defer conn.Close()

	c := make(chan interface{})
	b.sub(c)
	defer b.usub(c)

	for v := range c {
		if err := conn.WriteJSON(v); err != nil {
			log.Println(err)
			break
		}
	}
}
<|endoftext|>