<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
)

var (
	inputDir    string         // user input; top-level path to search
	searchText  string         // user input; keyword to search
	verbose     bool           // user input; if true displays all paths
	numFound    int            // # of files matching keyword
	fileVisit   int            // # of files visited by search
	dirFound    int            // # of directories matching keyword
	folderVisit int            // # of folders visited by search
	wg          sync.WaitGroup // sync goroutines / channels
	lock        sync.Mutex     // control access to counters (race prevention)
	maxSize     int64          // max file size
	json        bool           // output in json if true
	help        bool           // display help if true
	// output      string         // output file name
)

// walkresult struct for result document
type walkresult struct {
	path    string
	name    string
	found   bool
	isDir   bool
	size    int64
	modTime time.Time
}

func usage() {
	// user messaging
	fmt.Println("==================================")
	fmt.Println("gosearch: A search-in-text utility written in Go.")
	fmt.Println("==================================")
	fmt.Println("Usage:")
	fmt.Println("    gosearch [OPTIONS] -p path -k keyword")
	flag.PrintDefaults()
}

func init() {
	// flag init
	flag.StringVar(&inputDir, "p", "", "Path of directory to search")
	flag.StringVar(&searchText, "k", "", "Keyword to search")
	flag.Int64Var(&maxSize, "s", 100, "Max file size to search in MB - optional")
	flag.BoolVar(&json, "j", false, "Output in JSON - optional")
	// flag.StringVar(&output, "o", "out.log", "Output file name - optional")
	flag.BoolVar(&verbose, "v", false, "Verbose = optional (prints all files searched)")
	flag.BoolVar(&help, "h", false, "Print help menu")
}

// duration keeps track of function elapsed time
func duration(start time.Time, name string) {
	elapsed := time.Since(start)
	log.Printf("func %s elapsed %s\n", name, elapsed)
}

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

func errorOut(message string) bool {
	fmt.Fprintln(os.Stderr, message)
	return false
}

// check path exists
func exists(path string) bool {
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// walkFiles walks all files and sub-directory paths
func walkFiles(directory string, keyword string, filesFound chan walkresult, done chan bool) {

	// launch goroutine to walk path; add wait count
	wg.Add(1)
	go func() {
		defer wg.Done()
		err := filepath.Walk(directory, func(path string, f os.FileInfo, err error) error {
			errorCheck(err)

			// if file launch main search process
			if !f.IsDir() {
				fileCount()

				// only launch search if file is under size limit,
				if f.Size() < maxSize*1024*1024 {
					wg.Add(1)
					go readFile(path, f, filesFound)
				} else {
					log.WithFields(log.Fields{
						"type": "file",
						"name": f.Name(),
						"path": path,
					}).Warn("Skip file too large: ", f.Size())
				}
			}

			// folder path, increment count
			folderCount()
			wg.Add(1)
			go searchPath(path, f, filesFound)
			return nil
		})

		// launch cleanup, but sync wait until goroutines complete
		go cleanup(filesFound, done)

		// check errors for walk func
		errorCheck(err)
		return
	}()
	return
}

// readFile puts contents of file in memory, starts search
func readFile(path string, f os.FileInfo, filesFound chan walkresult) {
	defer wg.Done()
	content, err := ioutil.ReadFile(path)
	if err != nil {
		if !verbose {
			return
		}
		log.WithFields(log.Fields{
			"type": "file",
			"name": f.Name(),
			"path": path,
		}).Warn("File cannot be read", f.Size())
		return
	}
	wg.Add(1)
	go searchFile(path, content, f, filesFound)
}

// searchFile parses the contents of file looking for keyword
func searchFile(path string, content []byte, f os.FileInfo, filesFound chan walkresult) {
	defer wg.Done()
	x := string(content)
	search := strings.Contains(x, searchText)
	switch search {
	case true:
		lock.Lock()
		numFound++
		lock.Unlock()
		found := true
		filesFound <- walkresult{path, f.Name(), found, f.IsDir(), f.Size(), f.ModTime()}
		return
	case false:
		found := false
		filesFound <- walkresult{path, f.Name(), found, f.IsDir(), f.Size(), f.ModTime()}
		return
	}
}

// searchPath searches match in file or folder name
func searchPath(path string, f os.FileInfo, filesFound chan walkresult) {
	defer wg.Done()
	search := strings.Contains(f.Name(), searchText)
	switch search {
	case true:
		if f.IsDir() {
			lock.Lock()
			dirFound++
			lock.Unlock()
		} else {
			lock.Lock()
			numFound++
			lock.Unlock()
		}
		found := true
		filesFound <- walkresult{path, f.Name(), found, f.IsDir(), f.Size(), f.ModTime()}
		return
	case false:
		found := false
		filesFound <- walkresult{path, f.Name(), found, f.IsDir(), f.Size(), f.ModTime()}
		return
	}
}

// folderCount keeps count of folders visited during search
func folderCount() {
	lock.Lock()
	folderVisit++
	lock.Unlock()
}

// fileCount keeps count of files visited during search
func fileCount() {
	lock.Lock()
	fileVisit++
	lock.Unlock()
}

// waits for goroutines to complete, sets done signal and closes channels
func cleanup(filesFound chan walkresult, done chan bool) {
	wg.Wait()
	close(filesFound)
	done <- true
	<-done
	close(done)
	return
}

// summary prints results, counts, lets user know search is done
func summary(searchText string, path string) {
	log.WithFields(log.Fields{
		"searchString":   searchText,  // text to search
		"path":           path,        // file path requeted to search
		"filesChecked":   fileVisit,   // num of files visited during search
		"foldersChecked": folderVisit, // num of folders visited during search
		"filesFound":     numFound,    // num of files that contain match for search string
		"foldersFound":   dirFound,    // num of folders that contain match for search string
	}).Info("Search completed")
}

func main() {
	// main timer
	defer duration(time.Now(), "main")

	// check args provided
	flag.Parse()
	ok := true

	if help == true {
		usage()
		os.Exit(1)
	}
	if inputDir == "" {
		ok = errorOut("ERROR: Missing path to directory")
	} else {
		// check path exists
		verify := exists(inputDir)
		if !verify {
			ok = errorOut("ERROR: Path provided does not exist.")
		}
	}
	if searchText == "" {
		ok = errorOut("ERROR: Missing keyword to search")
	}

	if !ok {
		usage()
		os.Exit(1)
	}

	// log set to JSON format
	if json == true {
		log.SetFormatter(&log.JSONFormatter{})
	} else {
		// The TextFormatter is default, you don't actually have to do this.
		log.SetFormatter(&log.TextFormatter{})
	}

	// output file definition
	/*
		out, err := os.OpenFile(output, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			log.Fatalf("error opening log file: %v", err)
		}
		defer out.Close()
		multi := io.MultiWriter(out, os.Stdout)
		log.SetOutput(multi)
	*/

	// create channels
	filesFound := make(chan walkresult)
	done := make(chan bool)

	// notify user search started
	log.WithFields(log.Fields{
		"searchString": searchText,
		"path":         inputDir,
	}).Info("Search started")

	// start search work
	go walkFiles(inputDir, searchText, filesFound, done)

	// receive channel results and print
loop:
	for {
		select {
		case print := <-filesFound:
			if (len(print.path) > 0) && verbose && (print.found == false) {
				switch print.isDir {
				case true:
					log.WithFields(log.Fields{
						"type": "folder",
						"name": print.name,
						"path": print.path,
					}).Info("Match not found")
				case false:
					log.WithFields(log.Fields{
						"type": "file",
						"name": print.name,
						"path": print.path,
					}).Info("Match not found")
				}
			}
			if print.found == true {
				switch print.isDir {
				case true:
					log.WithFields(log.Fields{
						"type": "folder",
						"name": print.name,
						"path": print.path,
					}).Info("Match found")
				case false:
					log.WithFields(log.Fields{
						"type": "file",
						"name": print.name,
						"path": print.path,
					}).Info("Match found")
				}

			}
		case <-done:
			done <- true
			break loop
		}
	}

	// print search summary, file counts
	summary(searchText, inputDir)
}
<commit_msg>Remove output option not ready.<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
)

var (
	inputDir    string         // user input; top-level path to search
	searchText  string         // user input; keyword to search
	verbose     bool           // user input; if true displays all paths
	numFound    int            // # of files matching keyword
	fileVisit   int            // # of files visited by search
	dirFound    int            // # of directories matching keyword
	folderVisit int            // # of folders visited by search
	wg          sync.WaitGroup // sync goroutines / channels
	lock        sync.Mutex     // control access to counters (race prevention)
	maxSize     int64          // max file size
	json        bool           // output in json if true
	help        bool           // display help if true
)

// walkresult struct for result document
type walkresult struct {
	path    string
	name    string
	found   bool
	isDir   bool
	size    int64
	modTime time.Time
}

func usage() {
	// user messaging
	fmt.Println("==================================")
	fmt.Println("gosearch: A search-in-text utility written in Go.")
	fmt.Println("==================================")
	fmt.Println("Usage:")
	fmt.Println("    gosearch [OPTIONS] -p path -k keyword")
	flag.PrintDefaults()
}

func init() {
	// flag init
	flag.StringVar(&inputDir, "p", "", "Path of directory to search")
	flag.StringVar(&searchText, "k", "", "Keyword to search")
	flag.Int64Var(&maxSize, "s", 100, "Max file size to search in MB - optional")
	flag.BoolVar(&json, "j", false, "Output in JSON - optional")
	flag.BoolVar(&verbose, "v", false, "Verbose = optional (prints all files searched)")
	flag.BoolVar(&help, "h", false, "Print help menu")
}

// duration keeps track of function elapsed time
func duration(start time.Time, name string) {
	elapsed := time.Since(start)
	log.Printf("func %s elapsed %s\n", name, elapsed)
}

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

func errorOut(message string) bool {
	fmt.Fprintln(os.Stderr, message)
	return false
}

// check path exists
func exists(path string) bool {
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// walkFiles walks all files and sub-directory paths
func walkFiles(directory string, keyword string, filesFound chan walkresult, done chan bool) {

	// launch goroutine to walk path; add wait count
	wg.Add(1)
	go func() {
		defer wg.Done()
		err := filepath.Walk(directory, func(path string, f os.FileInfo, err error) error {
			errorCheck(err)

			// if file launch main search process
			if !f.IsDir() {
				fileCount()

				// only launch search if file is under size limit,
				if f.Size() < maxSize*1024*1024 {
					wg.Add(1)
					go readFile(path, f, filesFound)
				} else {
					log.WithFields(log.Fields{
						"type": "file",
						"name": f.Name(),
						"path": path,
					}).Warn("Skip file too large: ", f.Size())
				}
			}

			// folder path, increment count
			folderCount()
			wg.Add(1)
			go searchPath(path, f, filesFound)
			return nil
		})

		// launch cleanup, but sync wait until goroutines complete
		go cleanup(filesFound, done)

		// check errors for walk func
		errorCheck(err)
		return
	}()
	return
}

// readFile puts contents of file in memory, starts search
func readFile(path string, f os.FileInfo, filesFound chan walkresult) {
	defer wg.Done()
	content, err := ioutil.ReadFile(path)
	if err != nil {
		if !verbose {
			return
		}
		log.WithFields(log.Fields{
			"type": "file",
			"name": f.Name(),
			"path": path,
		}).Warn("File cannot be read", f.Size())
		return
	}
	wg.Add(1)
	go searchFile(path, content, f, filesFound)
}

// searchFile parses the contents of file looking for keyword
func searchFile(path string, content []byte, f os.FileInfo, filesFound chan walkresult) {
	defer wg.Done()
	x := string(content)
	search := strings.Contains(x, searchText)
	switch search {
	case true:
		lock.Lock()
		numFound++
		lock.Unlock()
		found := true
		filesFound <- walkresult{path, f.Name(), found, f.IsDir(), f.Size(), f.ModTime()}
		return
	case false:
		found := false
		filesFound <- walkresult{path, f.Name(), found, f.IsDir(), f.Size(), f.ModTime()}
		return
	}
}

// searchPath searches match in file or folder name
func searchPath(path string, f os.FileInfo, filesFound chan walkresult) {
	defer wg.Done()
	search := strings.Contains(f.Name(), searchText)
	switch search {
	case true:
		if f.IsDir() {
			lock.Lock()
			dirFound++
			lock.Unlock()
		} else {
			lock.Lock()
			numFound++
			lock.Unlock()
		}
		found := true
		filesFound <- walkresult{path, f.Name(), found, f.IsDir(), f.Size(), f.ModTime()}
		return
	case false:
		found := false
		filesFound <- walkresult{path, f.Name(), found, f.IsDir(), f.Size(), f.ModTime()}
		return
	}
}

// folderCount keeps count of folders visited during search
func folderCount() {
	lock.Lock()
	folderVisit++
	lock.Unlock()
}

// fileCount keeps count of files visited during search
func fileCount() {
	lock.Lock()
	fileVisit++
	lock.Unlock()
}

// waits for goroutines to complete, sets done signal and closes channels
func cleanup(filesFound chan walkresult, done chan bool) {
	wg.Wait()
	close(filesFound)
	done <- true
	<-done
	close(done)
	return
}

// summary prints results, counts, lets user know search is done
func summary(searchText string, path string) {
	log.WithFields(log.Fields{
		"searchString":   searchText,  // text to search
		"path":           path,        // file path requeted to search
		"filesChecked":   fileVisit,   // num of files visited during search
		"foldersChecked": folderVisit, // num of folders visited during search
		"filesFound":     numFound,    // num of files that contain match for search string
		"foldersFound":   dirFound,    // num of folders that contain match for search string
	}).Info("Search completed")
}

func main() {
	// main timer
	defer duration(time.Now(), "main")

	// check args provided
	flag.Parse()
	ok := true

	if help == true {
		usage()
		os.Exit(1)
	}
	if inputDir == "" {
		ok = errorOut("ERROR: Missing path to directory")
	} else {
		// check path exists
		verify := exists(inputDir)
		if !verify {
			ok = errorOut("ERROR: Path provided does not exist.")
		}
	}
	if searchText == "" {
		ok = errorOut("ERROR: Missing keyword to search")
	}

	if !ok {
		usage()
		os.Exit(1)
	}

	// log set to JSON format
	if json == true {
		log.SetFormatter(&log.JSONFormatter{})
	} else {
		// The TextFormatter is default, you don't actually have to do this.
		log.SetFormatter(&log.TextFormatter{})
	}

	// create channels
	filesFound := make(chan walkresult)
	done := make(chan bool)

	// notify user search started
	log.WithFields(log.Fields{
		"searchString": searchText,
		"path":         inputDir,
	}).Info("Search started")

	// start search work
	go walkFiles(inputDir, searchText, filesFound, done)

	// receive channel results and print
loop:
	for {
		select {
		case print := <-filesFound:
			if (len(print.path) > 0) && verbose && (print.found == false) {
				switch print.isDir {
				case true:
					log.WithFields(log.Fields{
						"type": "folder",
						"name": print.name,
						"path": print.path,
					}).Info("Match not found")
				case false:
					log.WithFields(log.Fields{
						"type": "file",
						"name": print.name,
						"path": print.path,
					}).Info("Match not found")
				}
			}
			if print.found == true {
				switch print.isDir {
				case true:
					log.WithFields(log.Fields{
						"type": "folder",
						"name": print.name,
						"path": print.path,
					}).Info("Match found")
				case false:
					log.WithFields(log.Fields{
						"type": "file",
						"name": print.name,
						"path": print.path,
					}).Info("Match found")
				}

			}
		case <-done:
			done <- true
			break loop
		}
	}

	// print search summary, file counts
	summary(searchText, inputDir)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"net/http/httputil"
)

func printReqHandler(res http.ResponseWriter, req *http.Request) {
	reqDump, err := httputil.DumpRequest(req, true)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(string(reqDump))
}

func registerHandler(path string, handlerFunc func(http.ResponseWriter, *http.Request)) {
	if path != "" {
		fmt.Printf("Registering handler for %s", path)
		fmt.Println()
		http.HandleFunc(path, handlerFunc)
	}
}

func main() {
	fmt.Println("gosigsrv starting")
	fmt.Println()
	// Register handlers
	registerHandler("/", printReqHandler)

	// Start listening
	http.ListenAndServe(":8087", nil)
	fmt.Println()
	fmt.Println("gosigsrv exiting")
}
<commit_msg>Basics of sign_in, sign_out, wait, message<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"strings"

	"github.com/google/uuid"
)

type peerKind int

const (
	client peerKind = iota
	server
)

type peerInfo struct {
	Kind    peerKind
	Name    string
	ID      string
	Channel chan string
}

const PEER_ID_PARAM_NAME string = "peer_id"
const TO_PARAM_NAME string = "to"

const PEER_MESSAGES_BUFFER_SIZE int = 100

var peers = make(map[string]peerInfo)

func printReqHandler(res http.ResponseWriter, req *http.Request) {
	reqDump, err := httputil.DumpRequest(req, true)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(string(reqDump))
}

func registerHandler(path string, handlerFunc func(http.ResponseWriter, *http.Request)) {
	if path != "" {
		fmt.Printf("Registering handler for %s", path)
		fmt.Println()
		http.HandleFunc(path, handlerFunc)
	}
}

func setConnectionHeader(header http.Header, close bool) {
	if close {
		header.Set("Connection", "close")
	} else {
		header.Set("Connection", "keep-alive")
	}
}

func setVersionHeader(header http.Header) {
	header.Set("Server", "PeerConnectionTestServer/0.1g")
}

func setNoCacheHeader(header http.Header) {
	header.Set("Cache-Control", "no-cache")
}

func addCorsHeaders(header http.Header) {
	header.Set("Access-Control-Allow-Origin", "*")
	header.Set("Access-Control-Allow-Credentials", "true")
	header.Set("Access-Control-Allow-Methods", strings.Join([]string{"GET", "POST", "OPTIONS"}, ","))
	header.Set("Access-Control-Allow-Headers", strings.Join([]string{"Content-Type", "Content-Length", "Cache-Control", "Connection"}, ","))
	header.Set("Access-Control-Expose-Headers", strings.Join([]string{"Content-Length", "X-Peer-Id"}, ","))
}

func signinHandler(res http.ResponseWriter, req *http.Request) {
	setConnectionHeader(res.Header(), true)
	setNoCacheHeader(res.Header())
	setVersionHeader(res.Header())
	addCorsHeaders(res.Header())

	if req.Method != "GET" {
		http.Error(res, "Bad request", http.StatusBadRequest)
		return
	}
	var name string
	// Parse out peer name
	for k, v := range req.URL.Query() {
		if v[0] == "" {
			name = k
			break
		}
	}

	if name == "" {
		http.Error(res, "No name", http.StatusBadRequest)
		return
	}

	var peerInfo peerInfo
	peerInfo.Name = name
	peerInfo.Channel = make(chan string, PEER_MESSAGES_BUFFER_SIZE)

	// Determine peer type
	if strings.Index(name, "renderingserver_") == 0 {
		peerInfo.Kind = server
	}

	// Generate id
	uuid, err := uuid.NewRandom()
	if err != nil {
		http.Error(res, err.Error(), http.StatusInternalServerError)
	}

	peerInfo.ID = uuid.String()

	peers[peerInfo.ID] = peerInfo

	peerInfoString := fmt.Sprintf("%s,%s,1", peerInfo.Name, peerInfo.ID)
	peerInfoString += fmt.Sprintln()
	responseString := peerInfoString

	// Return above + current peers (filtered for oppositing type)
	for pID, pInfo := range peers {
		if pID != peerInfo.ID && pInfo.Kind != peerInfo.Kind {
			responseString += fmt.Sprintf("%s,%s,1", pInfo.Name, pInfo.ID)
			responseString += fmt.Sprintln()

			// Also notify these peers that the new one exists
			if len(pInfo.Channel) < cap(pInfo.Channel) {
				pInfo.Channel <- peerInfoString
			} else {
				// TODO: Figure out what to do when peeer message buffer fills up
			}
		}
	}
	res.WriteHeader(http.StatusOK)
	fmt.Fprintf(res, responseString)
	// http.Error(res, "Not implemented "+name+" "+uuid.String(), http.statusadd)
}

func signoutHandler(res http.ResponseWriter, req *http.Request) {
	if req.Method != "GET" {
		http.Error(res, "Bad request", http.StatusBadRequest)
		return
	}
	setConnectionHeader(res.Header(), true)
	setNoCacheHeader(res.Header())
	setVersionHeader(res.Header())
	addCorsHeaders(res.Header())
	var peerID string
	// Parse out peers id
	for k, v := range req.URL.Query() {
		if k == PEER_ID_PARAM_NAME {
			peerID = v[0]
		}
	}
	_, exists := peers[peerID]
	if !exists {
		http.Error(res, "Unknown peer", http.StatusBadRequest)
		return
	}
	delete(peers, peerID)
	res.WriteHeader(http.StatusOK)
}

func messageHandler(res http.ResponseWriter, req *http.Request) {
	if req.Method != "POST" {
		http.Error(res, "Bad request", http.StatusBadRequest)
		return
	}

	// Parse out from id
	// Parse out to id
	peerID, peerExists := req.URL.Query()[PEER_ID_PARAM_NAME]
	toID, toExists := req.URL.Query()[TO_PARAM_NAME]

	if !peerExists || !toExists {
		http.Error(res, "Missing Peer or To ID", http.StatusBadRequest)
		return
	}

	_, peerInfoExists := peers[peerID[0]]
	to, toInfoExists := peers[toID[0]]

	if !peerInfoExists || !toInfoExists {
		http.Error(res, "Invalid Peer or To ID", http.StatusBadRequest)
		return
	}

	requestData, err := ioutil.ReadAll(req.Body)
	if err != nil {
		http.Error(res, err.Error(), http.StatusInternalServerError)
	}
	requestString := string(requestData)
	defer req.Body.Close()
	// Look up channel for to id
	if len(to.Channel) == cap(to.Channel) {
		http.Error(res, "Invalid Peer or To ID", http.StatusServiceUnavailable)
		return
	}
	to.Channel <- requestString

	// Send message to channel for to id
	res.WriteHeader(http.StatusOK)
}

func waitHandler(res http.ResponseWriter, req *http.Request) {
	setConnectionHeader(res.Header(), true)
	setNoCacheHeader(res.Header())
	setVersionHeader(res.Header())
	addCorsHeaders(res.Header())

	if req.Method != "GET" {
		http.Error(res, "Bad request", http.StatusBadRequest)
		return
	}

	// Parse out peer id
	peerID, peerExists := req.URL.Query()[PEER_ID_PARAM_NAME]

	if !peerExists {
		http.Error(res, "Missing Peer ID", http.StatusBadRequest)
		return
	}

	peerInfo, peerInfoExists := peers[peerID[0]]

	if !peerInfoExists {
		http.Error(res, "Peer is backed up", http.StatusBadRequest)
		return
	}

	// Look up message channel for peers id
	// Wait for message to reply
	responseString := <-peerInfo.Channel

	res.WriteHeader(http.StatusOK)
	fmt.Fprint(res, responseString)
}

func main() {
	fmt.Println("gosigsrv starting")
	fmt.Println()
	// Register handlers
	registerHandler("/sign_in", signinHandler)
	registerHandler("/sign_out", signoutHandler)
	registerHandler("/message", messageHandler)
	registerHandler("/wait", waitHandler)
	registerHandler("/", printReqHandler)

	// Start listening
	http.ListenAndServe(":8087", nil)
	fmt.Println()
	fmt.Println("gosigsrv exiting")
}
<|endoftext|>


<commit_before>temp
<commit_msg>moi krivie ruki<commit_after>package main

import (
	"bufio"
	"bytes"
	"encoding/xml"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

func EscapeText(text string) string {
	buf := new(bytes.Buffer)
	xml.EscapeText(buf, []byte(text))
	return buf.String()
}

func DataRace(logPath string) {
	writer := bufio.NewWriter(os.Stdout)

	buf := new(bytes.Buffer)
	err := filepath.Walk(filepath.Dir(logPath), func(path string, info os.FileInfo, err error) error {
		if err == nil && !info.IsDir() && strings.HasPrefix(path, logPath) {
			bytes, err := ioutil.ReadFile(path)
			if err != nil {
				log.Fatal(err)
			}
			buf.Write(bytes)
			buf.WriteString("\n")
			buf.WriteString("\n")
		}
		return err
	})
	if err != nil {
		log.Fatal(err)
	}

	writer.WriteString(`<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title>Data race report</title></head><body><pre>`)
	writer.WriteString("\n")
	writer.WriteString(EscapeText(buf.String()))
	writer.WriteString("\n")
	writer.WriteString(`</pre></body></html>`)

	if err := writer.Flush(); err != nil {
		log.Fatal(err)
	}
}

func ErrCheck(inFilePath string) {
	// go get github.com/kisielk/errcheck
	// errcheck ./...

	inFile, err := os.Open(inFilePath)
	if err != nil {
		log.Fatal(err)
	}
	defer inFile.Close()

	writer := bufio.NewWriter(os.Stdout)

	writer.WriteString(`<checkstyle version="4.3">`)
	writer.WriteString("\n")
	scanner := bufio.NewScanner(inFile)
	var prevFileName string
	for scanner.Scan() {
		origLine := scanner.Text()
		line := origLine
		index := strings.Index(line, "\t")
		if index == -1 {
			log.Fatalf(`Could not parse line "%s"`, origLine)
		}
		message := line[index+1:]
		line = line[:index]
		index1 := strings.Index(line, ":")
		index2 := strings.LastIndex(line, ":")
		if index1 == -1 || index2 == -1 {
			log.Fatalf(`Could not parse line "%s"`, origLine)
		}
		fileName := line[:index1]
		lineNumber := line[index1+1 : index2]
		columnNumber := line[index2+1:]

		if prevFileName != fileName {
			if prevFileName != "" {
				writer.WriteString("\t</file>\n")
			}
			writer.WriteString("\t")
			writer.WriteString(fmt.Sprintf(`<file name="%s">`, EscapeText(fileName)))
			writer.WriteString("\n")
		}
		writer.WriteString("\t\t")
		writer.WriteString(fmt.Sprintf(`<error line="%s" column="%s" severity="warning" message="%s"></error>`, lineNumber, columnNumber, EscapeText(message)))
		writer.WriteString("\n")

		prevFileName = fileName
	}
	if prevFileName != "" {
		writer.WriteString("\t</file>\n")
	}
	writer.WriteString("</checkstyle>\n")

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
	if err := writer.Flush(); err != nil {
		log.Fatal(err)
	}
}

func Vet(inFilePath string) {
	inFile, err := os.Open(inFilePath)
	if err != nil {
		log.Fatal(err)
	}
	defer inFile.Close()

	writer := bufio.NewWriter(os.Stdout)

	writer.WriteString(`<checkstyle version="4.3">`)
	writer.WriteString("\n")
	scanner := bufio.NewScanner(inFile)
	var prevFileName string
	for scanner.Scan() {
		origLine := scanner.Text()
		line := origLine
		if strings.Index(line, "exit status") != -1 {
			continue
		}
		index := strings.Index(line, ": ")
		if index == -1 {
			log.Fatalf(`Could not parse line "%s"`, origLine)
		}
		message := line[index+2:]
		line = line[:index]
		index = strings.LastIndex(line, ":")
		if index == -1 {
			log.Fatalf(`Could not parse line "%s"`, origLine)
		}
		fileName := line[:index]
		lineNumber := line[index+1:]

		if prevFileName != fileName {
			if prevFileName != "" {
				writer.WriteString("\t</file>\n")
			}
			writer.WriteString("\t")
			writer.WriteString(fmt.Sprintf(`<file name="%s">`, EscapeText(fileName)))
			writer.WriteString("\n")
		}
		writer.WriteString("\t\t")
		writer.WriteString(fmt.Sprintf(`<error line="%s" column="1" severity="warning" message="%s"></error>`, lineNumber, EscapeText(message)))
		writer.WriteString("\n")

		prevFileName = fileName
	}
	if prevFileName != "" {
		writer.WriteString("\t</file>\n")
	}
	writer.WriteString("</checkstyle>\n")

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
	if err := writer.Flush(); err != nil {
		log.Fatal(err)
	}
}

func main() {
	// /Users/akorzhevskii/Downloads/reports/report
	logPath := flag.String("log_path", "", "go -race log path")
	// /Users/akorzhevskii/Downloads/errcheck.out
	errcheckReport := flag.String("errcheck", "", "errcheck report")
	// /Users/akorzhevskii/Downloads/govet.out
	vetReport := flag.String("vet", "", "go vet report")
	flag.Parse()

	if *logPath != "" {
		DataRace(*logPath)
		return
	}

	if *errcheckReport != "" {
		ErrCheck(*errcheckReport)
		return
	}

	if *vetReport != "" {
		Vet(*vetReport)
		return
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package mop

import (
	`bytes`
	`fmt`
	`reflect`
	`regexp`
	`strings`
	`text/template`
	`time`
)

const TotalColumns = 15

type Column struct {
	width	   int
	name       string
	title	   string
	formatter  func(string)string
}

type Layout struct {
	columns          []Column
	regex            *regexp.Regexp
	market_template  *template.Template
	quotes_template  *template.Template
}

//-----------------------------------------------------------------------------
func (self *Layout) Initialize() *Layout {
	self.columns = []Column{
		{ -7, `Ticker`,    `Ticker`,    nil            },
		{ 10, `LastTrade`, `Last`,      currency       },
		{ 10, `Change`,    `Change`,    currency       },
		{ 10, `ChangePct`, `Change%`,   last           },
		{ 10, `Open`,      `Open`,      currency       },
		{ 10, `Low`,       `Low`,       currency       },
		{ 10, `High`,      `High`,      currency       },
		{ 10, `Low52`,     `52w Low`,   currency       },
		{ 10, `High52`,    `52w High`,  currency       },
		{ 11, `Volume`,    `Volume`,    nil            },
		{ 11, `AvgVolume`, `AvgVolume`, nil            },
		{  9, `PeRatio`,   `P/E`,       blank          },
		{  9, `Dividend`,  `Dividend`,  blank_currency },
		{  9, `Yield`,     `Yield`,     percent        },
		{ 11, `MarketCap`, `MktCap`,    currency       },
	}
	self.regex = regexp.MustCompile(`(\.\d+)[MB]?$`)
	self.market_template = build_market_template()
	self.quotes_template = build_quotes_template()

	return self
}

//-----------------------------------------------------------------------------
func (self *Layout) Market(market *Market) string {
	if ok, err := market.Ok(); !ok {
		return err
	}

	highlight(market.Dow, market.Sp500, market.Nasdaq)
	buffer := new(bytes.Buffer)
	self.market_template.Execute(buffer, market)

	return buffer.String()
}

//-----------------------------------------------------------------------------
func (self *Layout) Quotes(quotes *Quotes) string {
	if ok, err := quotes.Ok(); !ok {
		return err
	}

	vars := struct {
		Now    string
		Header string
		Stocks []Stock
	}{
		time.Now().Format(`3:04:05pm PST`),
		self.Header(quotes.profile),
		self.prettify(quotes),
	}

	buffer := new(bytes.Buffer)
	self.quotes_template.Execute(buffer, vars)

	return buffer.String()
}

//-----------------------------------------------------------------------------
func (self *Layout) Header(profile *Profile) string {
	str, selected_column := ``, profile.selected_column

	for i,col := range self.columns {
		arrow := arrow_for(i, profile)
		if i != selected_column {
			str += fmt.Sprintf(`%*s`, col.width, arrow + col.title)
		} else {
			str += fmt.Sprintf(`<r>%*s</r>`, col.width, arrow + col.title)
		}
	}

	return `<u>` + str + `</u>`
}

//-----------------------------------------------------------------------------
func (self *Layout) prettify(quotes *Quotes) []Stock {
	pretty := make([]Stock, len(quotes.stocks))
	//
	// Iterate over the list of stocks and properly format all its columns.
	//
	for i, stock := range quotes.stocks {
		pretty[i].Advancing = stock.Advancing
		//
		// Iterate over the list of stock columns. For each column name:
		// - Get current column value.
		// - If the column has the formatter method then call it.
		// - Set the column value padding it to the given width.
		//
		for _,column := range self.columns {
			// ex. value = stock.Change
			value := reflect.ValueOf(&stock).Elem().FieldByName(column.name).String()
			if column.formatter != nil {
				// ex. value = currency(value)
				value = column.formatter(value)
			}
			// ex. pretty[i].Change = self.pad(value, 10)
			reflect.ValueOf(&pretty[i]).Elem().FieldByName(column.name).SetString(self.pad(value, column.width))
		}
	}

	profile := quotes.profile
	new(Sorter).Initialize(profile).SortByCurrentColumn(pretty)
	//
	// Group stocks by advancing/declining unless sorted by Chanage or Change%
	// in which case the grouping is done already.
	//
	if profile.Grouped && (profile.SortColumn < 2 || profile.SortColumn > 3) {
		pretty = group(pretty)
	}

	return pretty
}

//-----------------------------------------------------------------------------
func (self *Layout) pad(str string, width int) string {
	match := self.regex.FindStringSubmatch(str)
	if len(match) > 0 {
		switch len(match[1]) {
		case 2:
			str = strings.Replace(str, match[1], match[1] + `0`, 1)
		case 4, 5:
			str = strings.Replace(str, match[1], match[1][0:3], 1)
		}
	}

	return fmt.Sprintf(`%*s`, width, str)
}

//-----------------------------------------------------------------------------
func build_market_template() *template.Template {
	markup := `{{.Dow.name}}: {{.Dow.change}} ({{.Dow.percent}}) at {{.Dow.latest}}, {{.Sp500.name}}: {{.Sp500.change}} ({{.Sp500.percent}}) at {{.Sp500.latest}}, {{.Nasdaq.name}}: {{.Nasdaq.change}} ({{.Nasdaq.percent}}) at {{.Nasdaq.latest}}
{{.Advances.name}}: {{.Advances.nyse}} ({{.Advances.nysep}}) on NYSE and {{.Advances.nasdaq}} ({{.Advances.nasdaqp}}) on Nasdaq. {{.Declines.name}}: {{.Declines.nyse}} ({{.Declines.nysep}}) on NYSE and {{.Declines.nasdaq}} ({{.Declines.nasdaqp}}) on Nasdaq {{if .IsClosed}}<right>U.S. markets closed</right>{{end}}
New highs: {{.Highs.nyse}} on NYSE and {{.Highs.nasdaq}} on Nasdaq. New lows: {{.Lows.nyse}} on NYSE and {{.Lows.nasdaq}} on Nasdaq.`

	return template.Must(template.New(`market`).Parse(markup))
}

//-----------------------------------------------------------------------------
func build_quotes_template() *template.Template {
	markup := `<right><white>{{.Now}}</></right>

{{.Header}}
{{range.Stocks}}{{if .Advancing}}<green>{{end}}{{.Ticker}}{{.LastTrade}}{{.Change}}{{.ChangePct}}{{.Open}}{{.Low}}{{.High}}{{.Low52}}{{.High52}}{{.Volume}}{{.AvgVolume}}{{.PeRatio}}{{.Dividend}}{{.Yield}}{{.MarketCap}}</>
{{end}}`

	return template.Must(template.New(`quotes`).Parse(markup))
}

//-----------------------------------------------------------------------------
func highlight(collections ...map[string]string) {
	for _, collection := range collections {
		if collection[`change`][0:1] != `-` {
			collection[`change`] = `<green>` + collection[`change`] + `</>`
		}
	}
}

//-----------------------------------------------------------------------------
func group(stocks []Stock) []Stock {
	grouped := make([]Stock, len(stocks))
	current := 0

	for _,stock := range stocks {
		if stock.Advancing {
			grouped[current] = stock
			current++
		}
	}
	for _,stock := range stocks {
		if !stock.Advancing {
			grouped[current] = stock
			current++
		}
	}

	return grouped
}

//-----------------------------------------------------------------------------
func arrow_for(column int, profile *Profile) string {
	if column == profile.SortColumn {
		if profile.Ascending {
			return string('\U00002191')
		}
		return string('\U00002193')
	}
	return ``
}

//-----------------------------------------------------------------------------
func blank(str string) string {
	if len(str) == 3 && str[0:3] == `N/A` {
		return `-`
	}

	return str
}

//-----------------------------------------------------------------------------
func blank_currency(str string) string {
	if str == `0.00` {
		return `-`
	}

	return currency(str)
}

//-----------------------------------------------------------------------------
func last(str string) string {
	if len(str) >= 6 && str[0:6] != `N/A - ` {
		return str
	}

	return str[6:]
}

//-----------------------------------------------------------------------------
func currency(str string) string {
	if str == `N/A` {
		return `-`
	}
	if sign := str[0:1]; sign == `+` || sign == `-` {
		return sign + `$` + str[1:]
	}

	return `$` + str
}

//-----------------------------------------------------------------------------
func percent(str string) string {
	if str == `N/A` {
		return `-`
	}

	return str + `%`
}
<commit_msg>Optimized sorter initialization<commit_after>// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package mop

import (
	`bytes`
	`fmt`
	`reflect`
	`regexp`
	`strings`
	`text/template`
	`time`
)

const TotalColumns = 15

type Column struct {
	width	   int
	name       string
	title	   string
	formatter  func(string)string
}

type Layout struct {
	columns          []Column
	sorter           *Sorter
	regex            *regexp.Regexp
	market_template  *template.Template
	quotes_template  *template.Template
}

//-----------------------------------------------------------------------------
func (self *Layout) Initialize() *Layout {
	self.columns = []Column{
		{ -7, `Ticker`,    `Ticker`,    nil            },
		{ 10, `LastTrade`, `Last`,      currency       },
		{ 10, `Change`,    `Change`,    currency       },
		{ 10, `ChangePct`, `Change%`,   last           },
		{ 10, `Open`,      `Open`,      currency       },
		{ 10, `Low`,       `Low`,       currency       },
		{ 10, `High`,      `High`,      currency       },
		{ 10, `Low52`,     `52w Low`,   currency       },
		{ 10, `High52`,    `52w High`,  currency       },
		{ 11, `Volume`,    `Volume`,    nil            },
		{ 11, `AvgVolume`, `AvgVolume`, nil            },
		{  9, `PeRatio`,   `P/E`,       blank          },
		{  9, `Dividend`,  `Dividend`,  blank_currency },
		{  9, `Yield`,     `Yield`,     percent        },
		{ 11, `MarketCap`, `MktCap`,    currency       },
	}
	self.regex = regexp.MustCompile(`(\.\d+)[MB]?$`)
	self.market_template = build_market_template()
	self.quotes_template = build_quotes_template()

	return self
}

//-----------------------------------------------------------------------------
func (self *Layout) Market(market *Market) string {
	if ok, err := market.Ok(); !ok {
		return err
	}

	highlight(market.Dow, market.Sp500, market.Nasdaq)
	buffer := new(bytes.Buffer)
	self.market_template.Execute(buffer, market)

	return buffer.String()
}

//-----------------------------------------------------------------------------
func (self *Layout) Quotes(quotes *Quotes) string {
	if ok, err := quotes.Ok(); !ok {
		return err
	}

	vars := struct {
		Now    string
		Header string
		Stocks []Stock
	}{
		time.Now().Format(`3:04:05pm PST`),
		self.Header(quotes.profile),
		self.prettify(quotes),
	}

	buffer := new(bytes.Buffer)
	self.quotes_template.Execute(buffer, vars)

	return buffer.String()
}

//-----------------------------------------------------------------------------
func (self *Layout) Header(profile *Profile) string {
	str, selected_column := ``, profile.selected_column

	for i,col := range self.columns {
		arrow := arrow_for(i, profile)
		if i != selected_column {
			str += fmt.Sprintf(`%*s`, col.width, arrow + col.title)
		} else {
			str += fmt.Sprintf(`<r>%*s</r>`, col.width, arrow + col.title)
		}
	}

	return `<u>` + str + `</u>`
}

//-----------------------------------------------------------------------------
func (self *Layout) prettify(quotes *Quotes) []Stock {
	pretty := make([]Stock, len(quotes.stocks))
	//
	// Iterate over the list of stocks and properly format all its columns.
	//
	for i, stock := range quotes.stocks {
		pretty[i].Advancing = stock.Advancing
		//
		// Iterate over the list of stock columns. For each column name:
		// - Get current column value.
		// - If the column has the formatter method then call it.
		// - Set the column value padding it to the given width.
		//
		for _,column := range self.columns {
			// ex. value = stock.Change
			value := reflect.ValueOf(&stock).Elem().FieldByName(column.name).String()
			if column.formatter != nil {
				// ex. value = currency(value)
				value = column.formatter(value)
			}
			// ex. pretty[i].Change = self.pad(value, 10)
			reflect.ValueOf(&pretty[i]).Elem().FieldByName(column.name).SetString(self.pad(value, column.width))
		}
	}

	profile := quotes.profile
	if self.sorter == nil { // Initialize sorter on first invocation.
		self.sorter = new(Sorter).Initialize(profile)
	}
	self.sorter.SortByCurrentColumn(pretty)
	//
	// Group stocks by advancing/declining unless sorted by Chanage or Change%
	// in which case the grouping has been done already.
	//
	if profile.Grouped && (profile.SortColumn < 2 || profile.SortColumn > 3) {
		pretty = group(pretty)
	}

	return pretty
}

//-----------------------------------------------------------------------------
func (self *Layout) pad(str string, width int) string {
	match := self.regex.FindStringSubmatch(str)
	if len(match) > 0 {
		switch len(match[1]) {
		case 2:
			str = strings.Replace(str, match[1], match[1] + `0`, 1)
		case 4, 5:
			str = strings.Replace(str, match[1], match[1][0:3], 1)
		}
	}

	return fmt.Sprintf(`%*s`, width, str)
}

//-----------------------------------------------------------------------------
func build_market_template() *template.Template {
	markup := `{{.Dow.name}}: {{.Dow.change}} ({{.Dow.percent}}) at {{.Dow.latest}}, {{.Sp500.name}}: {{.Sp500.change}} ({{.Sp500.percent}}) at {{.Sp500.latest}}, {{.Nasdaq.name}}: {{.Nasdaq.change}} ({{.Nasdaq.percent}}) at {{.Nasdaq.latest}}
{{.Advances.name}}: {{.Advances.nyse}} ({{.Advances.nysep}}) on NYSE and {{.Advances.nasdaq}} ({{.Advances.nasdaqp}}) on Nasdaq. {{.Declines.name}}: {{.Declines.nyse}} ({{.Declines.nysep}}) on NYSE and {{.Declines.nasdaq}} ({{.Declines.nasdaqp}}) on Nasdaq {{if .IsClosed}}<right>U.S. markets closed</right>{{end}}
New highs: {{.Highs.nyse}} on NYSE and {{.Highs.nasdaq}} on Nasdaq. New lows: {{.Lows.nyse}} on NYSE and {{.Lows.nasdaq}} on Nasdaq.`

	return template.Must(template.New(`market`).Parse(markup))
}

//-----------------------------------------------------------------------------
func build_quotes_template() *template.Template {
	markup := `<right><white>{{.Now}}</></right>

{{.Header}}
{{range.Stocks}}{{if .Advancing}}<green>{{end}}{{.Ticker}}{{.LastTrade}}{{.Change}}{{.ChangePct}}{{.Open}}{{.Low}}{{.High}}{{.Low52}}{{.High52}}{{.Volume}}{{.AvgVolume}}{{.PeRatio}}{{.Dividend}}{{.Yield}}{{.MarketCap}}</>
{{end}}`

	return template.Must(template.New(`quotes`).Parse(markup))
}

//-----------------------------------------------------------------------------
func highlight(collections ...map[string]string) {
	for _, collection := range collections {
		if collection[`change`][0:1] != `-` {
			collection[`change`] = `<green>` + collection[`change`] + `</>`
		}
	}
}

//-----------------------------------------------------------------------------
func group(stocks []Stock) []Stock {
	grouped := make([]Stock, len(stocks))
	current := 0

	for _,stock := range stocks {
		if stock.Advancing {
			grouped[current] = stock
			current++
		}
	}
	for _,stock := range stocks {
		if !stock.Advancing {
			grouped[current] = stock
			current++
		}
	}

	return grouped
}

//-----------------------------------------------------------------------------
func arrow_for(column int, profile *Profile) string {
	if column == profile.SortColumn {
		if profile.Ascending {
			return string('\U00002191')
		}
		return string('\U00002193')
	}
	return ``
}

//-----------------------------------------------------------------------------
func blank(str string) string {
	if len(str) == 3 && str[0:3] == `N/A` {
		return `-`
	}

	return str
}

//-----------------------------------------------------------------------------
func blank_currency(str string) string {
	if str == `0.00` {
		return `-`
	}

	return currency(str)
}

//-----------------------------------------------------------------------------
func last(str string) string {
	if len(str) >= 6 && str[0:6] != `N/A - ` {
		return str
	}

	return str[6:]
}

//-----------------------------------------------------------------------------
func currency(str string) string {
	if str == `N/A` {
		return `-`
	}
	if sign := str[0:1]; sign == `+` || sign == `-` {
		return sign + `$` + str[1:]
	}

	return `$` + str
}

//-----------------------------------------------------------------------------
func percent(str string) string {
	if str == `N/A` {
		return `-`
	}

	return str + `%`
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"
	"regexp"
	"sync"

	"github.com/nsf/termbox-go"

	"github.com/prattmic/lesser/lineio"
)

type size struct {
	x int
	y int
}

type Event int

const (
	// EventQuit requests an application exit.
	EventQuit Event = iota

	// EventRefresh requests a display refresh.
	EventRefresh
)

type Lesser struct {
	// src is the source file being displayed.
	src lineio.LineReader

	// tabStop is the number of spaces per tab.
	tabStop int

	// events is used to notify the main goroutine of events.
	events chan Event

	// mu locks the fields below.
	mu sync.Mutex

	// size is the size of the file display.
	// There is a statusbar beneath the display.
	size size

	// line is the line number of the first line of the display.
	line int64
}

// lastLine returns the last line on the display.  It may be beyond the end
// of the file, if the file is short enough.
// mu must be held on call.
func (l *Lesser) lastLine() int64 {
	return l.line + int64(l.size.y) - 1
}

// scrollUp moves the display up (i.e., decrements the first line number).
// You cannot scroll beyond the beginning of the file.
// refreshScreen must be called for the display to actually update.
func (l *Lesser) scrollUp() {
	l.mu.Lock()
	defer l.mu.Unlock()

	if l.line > 1 {
		l.line--
	}
}

// scrollDown moves the display down (i.e., increments the first line number).
// refreshScreen must be called for the display to actually update.
func (l *Lesser) scrollDown() {
	l.mu.Lock()
	defer l.mu.Unlock()

	// You can only scroll down if the next line exists.
	if l.src.LineExists(l.lastLine() + 1) {
		l.line++
	}
}

// scrollTop moves to the first line.
// refreshScreen must be called for the display to actually update.
func (l *Lesser) scrollTop() {
	l.mu.Lock()
	defer l.mu.Unlock()

	l.line = 1
}

// scrollBottom moves to the last line.
// refreshScreen must be called for the display to actually update.
func (l *Lesser) scrollBottom() {
	l.mu.Lock()
	defer l.mu.Unlock()

	l.line = 1

	// TODO(prattmic): binary search
	for l.src.LineExists(l.lastLine() + 1) {
		l.line++
	}
}

func (l *Lesser) listenEvents() {
	for {
		e := termbox.PollEvent()
		switch e.Type {
		case termbox.EventKey:
			switch e.Ch {
			case 'q':
				l.events <- EventQuit
			case 'j':
				l.scrollDown()
				l.events <- EventRefresh
			case 'k':
				l.scrollUp()
				l.events <- EventRefresh
			case 'g':
				l.scrollTop()
				l.events <- EventRefresh
			case 'G':
				l.scrollBottom()
				l.events <- EventRefresh
			case 's':
				l.search()
			}
		}
	}
}

type searchResult struct {
	line    int64
	matches [][]int
}

func (l *Lesser) search() {
	// TODO: search more than a fixed regexp
	reg := regexp.MustCompile("line")

	results := make(chan searchResult, 100)

	searchLine := func(line int64) {
		r, err := l.src.SearchLine(reg, line)
		if err != nil {
			r = nil
		}

		results <- searchResult{
			line:    line,
			matches: r,
		}
	}

	// TODO: search more than the first hundred lines :)
	for i := int64(1); i <= 100; i++ {
		go searchLine(i)
	}

	all := make([]searchResult, 0)
	for len(all) < 100 {
		all = append(all, <-results)
	}

	fmt.Fprintf(os.Stderr, "Results: %+v\n", all)
}

// statusBar renders the status bar.
// mu must be held on call.
func (l *Lesser) statusBar() {
	// The statusbar is just below the display.
	// For now, it just has a colon, followed by the cursor.
	termbox.SetCell(0, l.size.y, ':', 0, 0)
	termbox.SetCursor(1, l.size.y)
}

func (l *Lesser) refreshScreen() error {
	l.mu.Lock()
	defer l.mu.Unlock()

	for y := 0; y < l.size.y; y++ {
		buf := make([]byte, l.size.x)

		_, err := l.src.ReadLine(buf, l.line+int64(y))
		// EOF just means the line was shorter than the display.
		if err != nil && err != io.EOF {
			return err
		}

		var tabOffset int
		for i, c := range buf {
			// If there are tabs, we may get to the end of the
			// display before we run out of characters.
			if i >= l.size.x {
				break
			}

			if c == '\t' {
				// Clear the tab spaces
				for j := 0; j < l.tabStop; j++ {
					termbox.SetCell(tabOffset+j, y, ' ', 0, 0)
				}
				tabOffset += l.tabStop - 1
			} else {
				termbox.SetCell(tabOffset+i, y, rune(c), 0, 0)
			}
		}
	}

	l.statusBar()

	termbox.Flush()

	return nil
}

func (l *Lesser) Run() {
	go l.listenEvents()

	err := l.refreshScreen()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to refresh screen: %v\n", err)
		return
	}

	for {
		e := <-l.events

		switch e {
		case EventQuit:
			return
		case EventRefresh:
			err = l.refreshScreen()
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to refresh screen: %v\n", err)
				return
			}
		}
	}
}

func NewLesser(f *os.File, ts int) Lesser {
	x, y := termbox.Size()

	return Lesser{
		src:     lineio.NewLineReader(f),
		tabStop: ts,
		// Save one line for statusbar.
		size:   size{x: x, y: y - 1},
		line:   1,
		events: make(chan Event, 1),
	}
}
<commit_msg>Search entry<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"regexp"
	"sync"

	"github.com/nsf/termbox-go"

	"github.com/prattmic/lesser/lineio"
)

type size struct {
	x int
	y int
}

type Event int

const (
	// EventQuit requests an application exit.
	EventQuit Event = iota

	// EventRefresh requests a display refresh.
	EventRefresh
)

type Mode int

const (
	// ModeNormal is the standard mode, allowing file navigation.
	ModeNormal Mode = iota

	// ModeSearchEntry is search entry mode. Key presses are added
	// to the search string.
	ModeSearchEntry
)

type Lesser struct {
	// src is the source file being displayed.
	src lineio.LineReader

	// tabStop is the number of spaces per tab.
	tabStop int

	// events is used to notify the main goroutine of events.
	events chan Event

	// mu locks the fields below.
	mu sync.Mutex

	// size is the size of the file display.
	// There is a statusbar beneath the display.
	size size

	// line is the line number of the first line of the display.
	line int64

	// mode is the viewer mode.
	mode Mode

	// regexp is the search regexp specified by the user.
	// Must only be modified by the event goroutine.
	regexp string
}

// lastLine returns the last line on the display.  It may be beyond the end
// of the file, if the file is short enough.
// mu must be held on call.
func (l *Lesser) lastLine() int64 {
	return l.line + int64(l.size.y) - 1
}

// scrollUp moves the display up (i.e., decrements the first line number).
// You cannot scroll beyond the beginning of the file.
// refreshScreen must be called for the display to actually update.
func (l *Lesser) scrollUp() {
	l.mu.Lock()
	defer l.mu.Unlock()

	if l.line > 1 {
		l.line--
	}
}

// scrollDown moves the display down (i.e., increments the first line number).
// refreshScreen must be called for the display to actually update.
func (l *Lesser) scrollDown() {
	l.mu.Lock()
	defer l.mu.Unlock()

	// You can only scroll down if the next line exists.
	if l.src.LineExists(l.lastLine() + 1) {
		l.line++
	}
}

// scrollTop moves to the first line.
// refreshScreen must be called for the display to actually update.
func (l *Lesser) scrollTop() {
	l.mu.Lock()
	defer l.mu.Unlock()

	l.line = 1
}

// scrollBottom moves to the last line.
// refreshScreen must be called for the display to actually update.
func (l *Lesser) scrollBottom() {
	l.mu.Lock()
	defer l.mu.Unlock()

	l.line = 1

	// TODO(prattmic): binary search
	for l.src.LineExists(l.lastLine() + 1) {
		l.line++
	}
}

func (l *Lesser) handleEvent(e termbox.Event) {
	l.mu.Lock()
	mode := l.mode
	l.mu.Unlock()

	if e.Type != termbox.EventKey {
		return
	}

	c := e.Ch

	switch mode {
	case ModeNormal:
		switch c {
		case 'q':
			l.events <- EventQuit
		case 'j':
			l.scrollDown()
			l.events <- EventRefresh
		case 'k':
			l.scrollUp()
			l.events <- EventRefresh
		case 'g':
			l.scrollTop()
			l.events <- EventRefresh
		case 'G':
			l.scrollBottom()
			l.events <- EventRefresh
		case '/':
			l.mu.Lock()
			l.mode = ModeSearchEntry
			l.mu.Unlock()
			l.events <- EventRefresh
		}
	case ModeSearchEntry:
		switch c {
		case 0:
			switch e.Key {
			case termbox.KeyEnter:
				l.search(l.regexp)
				l.mu.Lock()
				l.mode = ModeNormal
				l.regexp = ""
				l.mu.Unlock()
				l.events <- EventRefresh
			}
		default:
			l.mu.Lock()
			l.regexp += string(c)
			l.mu.Unlock()
			l.events <- EventRefresh
		}
	}
}

func (l *Lesser) listenEvents() {
	for {
		e := termbox.PollEvent()
		l.handleEvent(e)
	}
}

type searchResult struct {
	line    int64
	matches [][]int
}

func (l *Lesser) search(s string) {
	reg, err := regexp.Compile(s)
	if err != nil {
		// TODO(prattmic): display a better error
		log.Printf("regexp failed to compile: %v", err)
		return
	}

	results := make(chan searchResult, 100)

	searchLine := func(line int64) {
		r, err := l.src.SearchLine(reg, line)
		if err != nil {
			r = nil
		}

		results <- searchResult{
			line:    line,
			matches: r,
		}
	}

	// TODO: search more than the first hundred lines :)
	for i := int64(1); i <= 100; i++ {
		go searchLine(i)
	}

	all := make([]searchResult, 0)
	for len(all) < 100 {
		all = append(all, <-results)
	}

	log.Printf("Results: %+v\n", all)
}

// statusBar renders the status bar.
// mu must be held on call.
func (l *Lesser) statusBar() {
	// The statusbar is just below the display.

	// Clear the statusbar
	for i := 0; i < l.size.x; i++ {
		termbox.SetCell(i, l.size.y, ' ', 0, 0)
	}

	switch l.mode {
	case ModeNormal:
		// Just a colon and a cursor
		termbox.SetCell(0, l.size.y, ':', 0, 0)
		termbox.SetCursor(1, l.size.y)
	case ModeSearchEntry:
		// / and search string
		termbox.SetCell(0, l.size.y, '/', 0, 0)
		for i, c := range l.regexp {
			termbox.SetCell(1+i, l.size.y, c, 0, 0)
		}
		termbox.SetCursor(1+len(l.regexp), l.size.y)
	}
}

func (l *Lesser) refreshScreen() error {
	l.mu.Lock()
	defer l.mu.Unlock()

	for y := 0; y < l.size.y; y++ {
		buf := make([]byte, l.size.x)

		_, err := l.src.ReadLine(buf, l.line+int64(y))
		// EOF just means the line was shorter than the display.
		if err != nil && err != io.EOF {
			return err
		}

		var tabOffset int
		for i, c := range buf {
			// If there are tabs, we may get to the end of the
			// display before we run out of characters.
			if i >= l.size.x {
				break
			}

			if c == '\t' {
				// Clear the tab spaces
				for j := 0; j < l.tabStop; j++ {
					termbox.SetCell(tabOffset+j, y, ' ', 0, 0)
				}
				tabOffset += l.tabStop - 1
			} else {
				termbox.SetCell(tabOffset+i, y, rune(c), 0, 0)
			}
		}
	}

	l.statusBar()

	termbox.Flush()

	return nil
}

func (l *Lesser) Run() {
	go l.listenEvents()

	err := l.refreshScreen()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to refresh screen: %v\n", err)
		return
	}

	for {
		e := <-l.events

		switch e {
		case EventQuit:
			return
		case EventRefresh:
			err = l.refreshScreen()
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to refresh screen: %v\n", err)
				return
			}
		}
	}
}

func NewLesser(f *os.File, ts int) Lesser {
	x, y := termbox.Size()

	return Lesser{
		src:     lineio.NewLineReader(f),
		tabStop: ts,
		// Save one line for statusbar.
		size:   size{x: x, y: y - 1},
		line:   1,
		events: make(chan Event, 1),
		mode:   ModeNormal,
	}
}
<|endoftext|>


<commit_before>// +build rpi

/*
  Go Language Raspberry Pi Interface
  (c) Copyright David Thorpe 2016-2017
  All Rights Reserved

  Documentation http://djthorpe.github.io/gopi/
  For Licensing and Usage information, please see LICENSE.md
*/

package rpi

import (
	"fmt"
)

////////////////////////////////////////////////////////////////////////////////
// TYPES

type Model uint
type Processor uint
type Revision uint

type Product struct {
	model       Model
	processor   Processor
	revision    Revision
	warrantyBit bool
}

////////////////////////////////////////////////////////////////////////////////
// CONSTANTS

const (
	RPI_WARRANTY_MASK     uint32 = 0x03000000
	RPI_ENCODING_MASK     uint32 = 0x00800000
	RPI_REVISION_MASK     uint32 = 0x0000000F
	RPI_MODEL_MASK        uint32 = 0x00000FF0
	RPI_PROCESSOR_MASK    uint32 = 0x0000F000
	RPI_MANUFACTURER_MASK uint32 = 0x000F0000
	RPI_MEMORY_MASK       uint32 = 0x00700000
)

const (
	RPI_MODEL_A                Model = (0x00 << 4)
	RPI_MODEL_B                Model = (0x01 << 4)
	RPI_MODEL_A_PLUS           Model = (0x02 << 4)
	RPI_MODEL_B_PLUS           Model = (0x03 << 4)
	RPI_MODEL_B_PI_2           Model = (0x04 << 4)
	RPI_MODEL_ALPHA            Model = (0x05 << 4)
	RPI_MODEL_COMPUTE_MODULE   Model = (0x06 << 4)
	RPI_MODEL_B_PI_3           Model = (0x08 << 4)
	RPI_MODEL_ZERO             Model = (0x09 << 4)
	RPI_MODEL_COMPUTE_MODULE_3 Model = (0x0A << 4)
	RPI_MODEL_ZERO_W           Model = (0x0C << 4)
	RPI_MODEL_UNKNOWN          Model = (0x0F << 4)
)

const (
	RPI_PROCESSOR_UNKNOWN Processor = 0xFFFFFFFF
	RPI_PROCESSOR_BCM2835 Processor = (0 << 12)
	RPI_PROCESSOR_BCM2836 Processor = (1 << 12)
	RPI_PROCESSOR_BCM2837 Processor = (2 << 12)
)

const (
	RPI_REVISION_UNKNOWN Revision = 0
)

////////////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES

var (
	productmap1 = map[uint32]Model{
		0x02: RPI_MODEL_B,
		0x03: RPI_MODEL_B,
		0x04: RPI_MODEL_B,
		0x05: RPI_MODEL_B,
		0x06: RPI_MODEL_B,
		0x07: RPI_MODEL_A,
		0x08: RPI_MODEL_A,
		0x09: RPI_MODEL_A,
		0x0D: RPI_MODEL_B,
		0x0E: RPI_MODEL_B,
		0x0F: RPI_MODEL_B,
		0x10: RPI_MODEL_B_PLUS,
		0x11: RPI_MODEL_COMPUTE_MODULE,
		0x12: RPI_MODEL_A_PLUS,
		0x13: RPI_MODEL_B_PLUS,
		0x14: RPI_MODEL_COMPUTE_MODULE,
		0x15: RPI_MODEL_A_PLUS,
	}
	pcbmap1 = map[uint32]Revision{
		0x02: Revision(1),
		0x03: Revision(1),
		0x04: Revision(2),
		0x05: Revision(2),
		0x06: Revision(2),
		0x07: Revision(2),
		0x08: Revision(2),
		0x09: Revision(2),
		0x0D: Revision(2),
		0x0E: Revision(2),
		0x0F: Revision(2),
		0x10: Revision(1),
		0x11: Revision(1),
		0x12: Revision(1),
		0x13: Revision(1),
		0x14: Revision(1),
		0x15: Revision(1),
	}
)

////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS

func (this *hardware) GetProduct() (*Product, error) {
	if r, err := this.GetRevisionUint32(); err != nil {
		return nil, err
	} else {
		model, revision := modelRevision(r)
		return &Product{model, processor(r), revision, warrantyBit(r)}, nil
	}
}

////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS

// warrantyBit returns the value of the warranty bit
func warrantyBit(revision uint32) bool {
	return ((revision & uint32(RPI_WARRANTY_MASK)) != 0)
}

// modelRevision returns model and revision information
func modelRevision(revision uint32) (Model, Revision) {
	revision = revision & ^RPI_WARRANTY_MASK
	if (revision & RPI_ENCODING_MASK) != 0 {
		// Raspberry Pi 2 style revision coding
		return Model(revision & RPI_MODEL_MASK), Revision(revision & RPI_REVISION_MASK)
	} else {
		// Raspberry Pi 1 style revision coding
		if model, ok := productmap1[revision]; !ok {
			return RPI_MODEL_UNKNOWN, RPI_REVISION_UNKNOWN
		} else if pcb, ok := pcbmap1[revision]; !ok {
			return RPI_MODEL_UNKNOWN, RPI_REVISION_UNKNOWN
		} else {
			return model, pcb
		}
	}
}

// processor returns processor information
func processor(revision uint32) Processor {
	revision = revision & ^RPI_WARRANTY_MASK
	if (revision & RPI_ENCODING_MASK) != 0 {
		// Raspberry Pi 2 style revision coding
		return Processor(revision & RPI_PROCESSOR_MASK)
	} else {
		// Raspberry Pi 1 style revision coding
		return RPI_PROCESSOR_BCM2835
	}
}

////////////////////////////////////////////////////////////////////////////////
// STRINGIFY

func (p *Product) String() string {
	return fmt.Sprintf("sys.hw.rpi.Product{ model=%v processor=%v revision=%v warrantyBit=%v }", p.model, p.processor, p.revision, p.warrantyBit)
}

func (m Model) String() string {
	switch m {
	case RPI_MODEL_A:
		return "RPI_MODEL_A"
	case RPI_MODEL_B:
		return "RPI_MODEL_B"
	case RPI_MODEL_A_PLUS:
		return "RPI_MODEL_A_PLUS"
	case RPI_MODEL_B_PLUS:
		return "RPI_MODEL_B_PLUS"
	case RPI_MODEL_B_PI_2:
		return "RPI_MODEL_B_PI_2"
	case RPI_MODEL_B_PI_3:
		return "RPI_MODEL_B_PI_3"
	case RPI_MODEL_ALPHA:
		return "RPI_MODEL_ALPHA"
	case RPI_MODEL_COMPUTE_MODULE:
		return "RPI_MODEL_COMPUTE_MODULE"
	case RPI_MODEL_COMPUTE_MODULE_3:
		return "RPI_MODEL_COMPUTE_MODULE_3"
	case RPI_MODEL_ZERO:
		return "RPI_MODEL_ZERO"
	case RPI_MODEL_ZERO_W:
		return "RPI_MODEL_ZERO_W"
	default:
		return fmt.Sprintf("[?? Unknown Model value 0x%02X]", uint32(m))
	}
}

func (p Processor) String() string {
	switch p {
	case RPI_PROCESSOR_BCM2835:
		return "RPI_PROCESSOR_BCM2835"
	case RPI_PROCESSOR_BCM2836:
		return "RPI_PROCESSOR_BCM2836"
	case RPI_PROCESSOR_BCM2837:
		return "RPI_PROCESSOR_BCM2837"
	default:
		return "[?? Unknown Processor value]"
	}
}

func (p Revision) String() string {
	if p == RPI_REVISION_UNKNOWN {
		return "[?? Unknwon Revision value]"
	}
	return fmt.Sprintf("RPI_REVISION_V%v", uint(p))
}
<commit_msg>Added B Plus to the models<commit_after>// +build rpi

/*
  Go Language Raspberry Pi Interface
  (c) Copyright David Thorpe 2016-2017
  All Rights Reserved

  Documentation http://djthorpe.github.io/gopi/
  For Licensing and Usage information, please see LICENSE.md
*/

package rpi

import (
	"fmt"
)

////////////////////////////////////////////////////////////////////////////////
// TYPES

type Model uint
type Processor uint
type Revision uint

type Product struct {
	model       Model
	processor   Processor
	revision    Revision
	warrantyBit bool
}

////////////////////////////////////////////////////////////////////////////////
// CONSTANTS

const (
	RPI_WARRANTY_MASK     uint32 = 0x03000000
	RPI_ENCODING_MASK     uint32 = 0x00800000
	RPI_REVISION_MASK     uint32 = 0x0000000F
	RPI_MODEL_MASK        uint32 = 0x00000FF0
	RPI_PROCESSOR_MASK    uint32 = 0x0000F000
	RPI_MANUFACTURER_MASK uint32 = 0x000F0000
	RPI_MEMORY_MASK       uint32 = 0x00700000
)

const (
	RPI_MODEL_A                Model = (0x00 << 4)
	RPI_MODEL_B                Model = (0x01 << 4)
	RPI_MODEL_A_PLUS           Model = (0x02 << 4)
	RPI_MODEL_B_PLUS           Model = (0x03 << 4)
	RPI_MODEL_B_2              Model = (0x04 << 4)
	RPI_MODEL_ALPHA            Model = (0x05 << 4)
	RPI_MODEL_COMPUTE_MODULE   Model = (0x06 << 4)
	RPI_MODEL_B_3              Model = (0x08 << 4)
	RPI_MODEL_ZERO             Model = (0x09 << 4)
	RPI_MODEL_COMPUTE_MODULE_3 Model = (0x0A << 4)
	RPI_MODEL_ZERO_W           Model = (0x0C << 4)
	RPI_MODEL_B_3PLUS          Model = (0x0D << 4)
	RPI_MODEL_UNKNOWN          Model = (0x0F << 4)
)

const (
	RPI_PROCESSOR_UNKNOWN Processor = 0xFFFFFFFF
	RPI_PROCESSOR_BCM2835 Processor = (0 << 12)
	RPI_PROCESSOR_BCM2836 Processor = (1 << 12)
	RPI_PROCESSOR_BCM2837 Processor = (2 << 12)
)

const (
	RPI_REVISION_UNKNOWN Revision = 0
)

////////////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES

var (
	productmap1 = map[uint32]Model{
		0x02: RPI_MODEL_B,
		0x03: RPI_MODEL_B,
		0x04: RPI_MODEL_B,
		0x05: RPI_MODEL_B,
		0x06: RPI_MODEL_B,
		0x07: RPI_MODEL_A,
		0x08: RPI_MODEL_A,
		0x09: RPI_MODEL_A,
		0x0D: RPI_MODEL_B,
		0x0E: RPI_MODEL_B,
		0x0F: RPI_MODEL_B,
		0x10: RPI_MODEL_B_PLUS,
		0x11: RPI_MODEL_COMPUTE_MODULE,
		0x12: RPI_MODEL_A_PLUS,
		0x13: RPI_MODEL_B_PLUS,
		0x14: RPI_MODEL_COMPUTE_MODULE,
		0x15: RPI_MODEL_A_PLUS,
	}
	pcbmap1 = map[uint32]Revision{
		0x02: Revision(1),
		0x03: Revision(1),
		0x04: Revision(2),
		0x05: Revision(2),
		0x06: Revision(2),
		0x07: Revision(2),
		0x08: Revision(2),
		0x09: Revision(2),
		0x0D: Revision(2),
		0x0E: Revision(2),
		0x0F: Revision(2),
		0x10: Revision(1),
		0x11: Revision(1),
		0x12: Revision(1),
		0x13: Revision(1),
		0x14: Revision(1),
		0x15: Revision(1),
	}
)

////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS

func (this *hardware) GetProduct() (*Product, error) {
	if r, err := this.GetRevisionUint32(); err != nil {
		return nil, err
	} else {
		model, revision := modelRevision(r)
		return &Product{model, processor(r), revision, warrantyBit(r)}, nil
	}
}

////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS

// warrantyBit returns the value of the warranty bit
func warrantyBit(revision uint32) bool {
	return ((revision & uint32(RPI_WARRANTY_MASK)) != 0)
}

// modelRevision returns model and revision information
func modelRevision(revision uint32) (Model, Revision) {
	revision = revision & ^RPI_WARRANTY_MASK
	if (revision & RPI_ENCODING_MASK) != 0 {
		// Raspberry Pi 2 style revision coding
		return Model(revision & RPI_MODEL_MASK), Revision(revision & RPI_REVISION_MASK)
	} else {
		// Raspberry Pi 1 style revision coding
		if model, ok := productmap1[revision]; !ok {
			return RPI_MODEL_UNKNOWN, RPI_REVISION_UNKNOWN
		} else if pcb, ok := pcbmap1[revision]; !ok {
			return RPI_MODEL_UNKNOWN, RPI_REVISION_UNKNOWN
		} else {
			return model, pcb
		}
	}
}

// processor returns processor information
func processor(revision uint32) Processor {
	revision = revision & ^RPI_WARRANTY_MASK
	if (revision & RPI_ENCODING_MASK) != 0 {
		// Raspberry Pi 2 style revision coding
		return Processor(revision & RPI_PROCESSOR_MASK)
	} else {
		// Raspberry Pi 1 style revision coding
		return RPI_PROCESSOR_BCM2835
	}
}

////////////////////////////////////////////////////////////////////////////////
// STRINGIFY

func (p *Product) String() string {
	return fmt.Sprintf("sys.hw.rpi.Product{ model=%v processor=%v revision=%v warrantyBit=%v }", p.model, p.processor, p.revision, p.warrantyBit)
}

func (m Model) String() string {
	switch m {
	case RPI_MODEL_A:
		return "RPI_MODEL_A"
	case RPI_MODEL_B:
		return "RPI_MODEL_B"
	case RPI_MODEL_A_PLUS:
		return "RPI_MODEL_A_PLUS"
	case RPI_MODEL_B_PLUS:
		return "RPI_MODEL_B_PLUS"
	case RPI_MODEL_B_2:
		return "RPI_MODEL_B_2"
	case RPI_MODEL_B_3:
		return "RPI_MODEL_B_3"
	case RPI_MODEL_ALPHA:
		return "RPI_MODEL_ALPHA"
	case RPI_MODEL_COMPUTE_MODULE:
		return "RPI_MODEL_COMPUTE_MODULE"
	case RPI_MODEL_COMPUTE_MODULE_3:
		return "RPI_MODEL_COMPUTE_MODULE_3"
	case RPI_MODEL_ZERO:
		return "RPI_MODEL_ZERO"
	case RPI_MODEL_ZERO_W:
		return "RPI_MODEL_ZERO_W"
	case RPI_MODEL_B_3PLUS:
		return "RPI_MODEL_B_3PLUS"
	default:
		return fmt.Sprintf("[?? Unknown Model value 0x%02X]", uint32(m))
	}
}

func (p Processor) String() string {
	switch p {
	case RPI_PROCESSOR_BCM2835:
		return "RPI_PROCESSOR_BCM2835"
	case RPI_PROCESSOR_BCM2836:
		return "RPI_PROCESSOR_BCM2836"
	case RPI_PROCESSOR_BCM2837:
		return "RPI_PROCESSOR_BCM2837"
	default:
		return "[?? Unknown Processor value]"
	}
}

func (p Revision) String() string {
	if p == RPI_REVISION_UNKNOWN {
		return "[?? Unknwon Revision value]"
	}
	return fmt.Sprintf("RPI_REVISION_V%v", uint(p))
}
<|endoftext|>


<commit_before>package main
import (
   "os/exec"
   "os"
   "log"
   "flag"
   "syscall"
    )

func main() {
    gid := flag.Int("gid", os.Getgid(), "The user id of the user you wish to switch to")
    uid := flag.Int("uid", os.Getuid(), "The user id of the user you wish to switch to")
    flag.Parse()
    err := syscall.Setgid(*gid)
    if err != nil {
        log.Fatalf("Failed to switch to group id %d: %v\n", *gid, err)
    }
    err = syscall.Setuid(*uid)
    if err != nil {
        log.Fatalf("Failed to switch to user id %d: %v\n", *uid, err)
    }
    args := flag.Args()
    if len(args) != 0 {
        cmd := exec.Command(args[0])
        cmd.Args = args;
        cmd.Stdin = os.Stdin
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        cmd.Start()
        err := cmd.Wait()
        if err != nil {
            log.Fatal(err)
        }
    }
}<commit_msg>affirm that we are that user before running<commit_after>package main
import (
   "os/exec"
   "os"
   "log"
   "flag"
   "syscall"
    )

func main() {
    gid := flag.Int("gid", os.Getgid(), "The user id of the user you wish to switch to")
    uid := flag.Int("uid", os.Getuid(), "The user id of the user you wish to switch to")
    flag.Parse()
    err := syscall.Setgid(*gid)
    if err != nil {
        log.Fatalf("Failed to switch to group id %d: %v\n", *gid, err)
        os.Exit(1)
    }
    err = syscall.Setuid(*uid)
    if err != nil {
        log.Fatalf("Failed to switch to user id %d: %v\n", *uid, err)
        os.Exit(1)
    }
    actual_uid := os.Getuid()
    if actual_uid != *uid {
        log.Fatalf("Failed to switch to user id %d. Instead am %d\n", *uid, actual_uid)
        os.Exit(1)
    }
    actual_gid := os.Getgid()
    if actual_gid != *gid {
        log.Fatalf("Failed to switch to group id %d. Instead am %d\n", *gid, actual_gid)
        os.Exit(1)
    }
    args := flag.Args()
    if len(args) != 0 {
        cmd := exec.Command(args[0])
        cmd.Args = args;
        cmd.Stdin = os.Stdin
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        cmd.Start()
        err := cmd.Wait()
        if err != nil {
            log.Fatal(err)
        }
    }
}<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"github.com/xyproto/moskus"
)

type WorkDayAndLocation struct {
	dayoftheweek time.Weekday
	fromHour     int
	uptoHour     int
	location     string
}

type PersonPlan struct {
	who      string
	workdays []*WorkDayAndLocation
}

type PeriodPlan struct {
	year        int
	fromMonth   int
	uptoMonth   int
	personPlans []*PersonPlan
}

func NewPersonPlan(who string) *PersonPlan {
	var pp PersonPlan
	pp.who = who
	return &pp
}

func (pp *PersonPlan) AddWorkday(dayoftheweek time.Weekday, fromHour, uptoHour int, location string) {
	newday := &WorkDayAndLocation{dayoftheweek, fromHour, uptoHour, location}
	pp.workdays = append(pp.workdays, newday)
}

func (pp *PersonPlan) String() string {
	cal, err := moskus.NewCalendar("nb_NO", true)
	if err != nil {
		panic("No calendar available for nb_NO")
	}
	s := "User: " + pp.who + "\n"
	s += "-----------------------------------------------\n"
	for _, day := range pp.workdays {
		s += "\n"
		s += "\t" + day.dayoftheweek.String() + " (" + cal.DayName(day.dayoftheweek) + ")\n"
		s += fmt.Sprintf("\tFrom this hour: \t%d\n", day.fromHour)
		s += fmt.Sprintf("\tUp to this hour:\t%d\n", day.uptoHour)
		s += fmt.Sprintf("\tAt this location:\t%s\n", day.location)
	}
	return s
}

func NewPeriodPlan(year, fromMonth, uptoMonth int) *PeriodPlan {
	var pps []*PersonPlan
	return &PeriodPlan{year, fromMonth, uptoMonth, pps}
}

func (pp *PeriodPlan) AddPersonPlan(persplan *PersonPlan) {
	pp.personPlans = append(pp.personPlans, persplan)
}

func (pp *PeriodPlan) ForAllWeekdays(fn func(string, time.Weekday, int, string) string) string {
	s := ""
	for day := 0; day < 7; day++ {
		for hour := 8; hour < 21; hour++ {
			for _, persplan := range pp.personPlans {
				for _, personday := range persplan.workdays {
					if personday.dayoftheweek == time.Weekday(day) {
						if (hour >= personday.fromHour) && (hour < personday.uptoHour) {
							s += fn(persplan.who, time.Weekday(day), hour, personday.location)
						}
					}
				}
			}
		}
	}
	return s
}

func infoline(who string, weekday time.Weekday, hour int, location string) string {
	return fmt.Sprintf("%s on %s hour that starts at %d at %s\n", who, weekday, hour, location)
}

func (pp *PeriodPlan) String() string {
	s := fmt.Sprintf("From %d, month %d\n", pp.year, pp.fromMonth)
	s += fmt.Sprintf("Up to %d, month %d\n", pp.year, pp.uptoMonth)
	s += pp.ForAllWeekdays(infoline)
	return s
}

// TODO: Broken, fix
// TODO: Return an HourInfo struct or something instead
func (pp *PeriodPlan) HourInfo(t time.Time) string {
	s := ""
	for dayoftheweek := 0; dayoftheweek < 7; dayoftheweek++ {
		for _, persplan := range pp.personPlans {
			for _, personday := range persplan.workdays {
				// Right day of the week?
				if personday.dayoftheweek == t.Weekday() {
					s += fmt.Sprintf("%s on %s, at the hour that starts at %d, at %s\n", persplan.who, t.Weekday(), t.Hour(), personday.location)
				}
			}
		}
	}
	return s
}

// TODO: Broken, fix
// Find info for a given hour for all given period plans
// Note that if period plans are overlapping, this function will only return info for the first hour it finds!
// TODO: Separate day and day of the week more clearly
func HourInfo(allPlans []*PeriodPlan, t time.Time) string {
	fmt.Printf("year %d, month %d, day of the month %d, hour %d\n", t.Year(), t.Month(), t.Day(), t.Hour())
	for _, pp := range allPlans {
		if (pp.year == t.Year()) && (pp.fromMonth <= int(t.Month())) && (int(t.Month()) < pp.uptoMonth) {
			return pp.HourInfo(t)
		}
	}
	// TODO: Introduce err
	return "No hour info found"
}

func main() {
	ppAlexander := NewPersonPlan("Alexander")
	ppAlexander.AddWorkday(time.Monday, 8, 15, "KNH")     // monday, from 8, up to 15
	ppAlexander.AddWorkday(time.Wednesday, 12, 17, "KOH") // wednesday, from 12, up to 17

	fmt.Println(ppAlexander.String())

	ppBob := NewPersonPlan("Bob")
	ppBob.AddWorkday(time.Tuesday, 9, 11, "KOH")  // monday, from 9, up to 11
	ppBob.AddWorkday(time.Thursday, 8, 10, "KNH") // wednesday, from 8, up to 10

	fmt.Println(ppBob.String())

	periodplan := NewPeriodPlan(2013, 1, 8)
	periodplan.AddPersonPlan(ppAlexander)
	periodplan.AddPersonPlan(ppBob)

	fmt.Println(periodplan.String())

	fmt.Println("Hour info:")
	fmt.Println(periodplan.HourInfo(time.Date(2013, 3, 1, 12, 0, 0, 0, time.UTC)))

	var allPlans []*PeriodPlan
	allPlans = append(allPlans, periodplan)

	fmt.Println("Hour info for all plans:")
	fmt.Println(HourInfo(allPlans, time.Date(2013, 3, 3, 15, 0, 0, 0, time.UTC))) // y m d h
}
<commit_msg>Now getting info for a specific hour works!<commit_after>package main

import (
	"fmt"
	"time"

	"github.com/xyproto/moskus"
)

const (
	FIRSTHOUR = 8
	LASTHOUR = 22
)

// Info about one thing that can happen during an hour
type HourInfoSnippet struct {
	who string
	when time.Time
	where string
}

// Info about everything that happens during an hour
type HourInfo []*HourInfoSnippet

// A plan is a collecion of plans for just a few months at a time
type Plans struct {
	all []*PeriodPlan
}

type WorkDayAndLocation struct {
	dayoftheweek time.Weekday
	fromHour     int
	uptoHour     int
	location     string
}

type PersonPlan struct {
	who      string
	workdays []*WorkDayAndLocation
}

type PeriodPlan struct {
	year        int
	fromMonth   int
	uptoMonth   int
	personPlans []*PersonPlan
}

func NewPersonPlan(who string) *PersonPlan {
	var pp PersonPlan
	pp.who = who
	return &pp
}

func (pp *PersonPlan) AddWorkday(dayoftheweek time.Weekday, fromHour, uptoHour int, location string) {
	newday := &WorkDayAndLocation{dayoftheweek, fromHour, uptoHour, location}
	pp.workdays = append(pp.workdays, newday)
}

func (pp *PersonPlan) String() string {
	cal, err := moskus.NewCalendar("nb_NO", true)
	if err != nil {
		panic("No calendar available for nb_NO")
	}
	s := "User: " + pp.who + "\n"
	s += "-----------------------------------------------\n"
	for _, day := range pp.workdays {
		s += "\n"
		s += "\t" + day.dayoftheweek.String() + " (" + cal.DayName(day.dayoftheweek) + ")\n"
		s += fmt.Sprintf("\tFrom this hour: \t%d\n", day.fromHour)
		s += fmt.Sprintf("\tUp to this hour:\t%d\n", day.uptoHour)
		s += fmt.Sprintf("\tAt this location:\t%s\n", day.location)
	}
	return s
}

func NewPeriodPlan(year, fromMonth, uptoMonth int) *PeriodPlan {
	var pps []*PersonPlan
	return &PeriodPlan{year, fromMonth, uptoMonth, pps}
}

func (pp *PeriodPlan) AddPersonPlan(persplan *PersonPlan) {
	pp.personPlans = append(pp.personPlans, persplan)
}

func (pp *PeriodPlan) ForAllWeekdays(fn func(string, time.Weekday, int, string) string) string {
	s := ""
	for day := 0; day < 7; day++ {
		for hour := FIRSTHOUR; hour <= LASTHOUR; hour++ {
			for _, persplan := range pp.personPlans {
				for _, personday := range persplan.workdays {
					if personday.dayoftheweek == time.Weekday(day) {
						if (hour >= personday.fromHour) && (hour < personday.uptoHour) {
							s += fn(persplan.who, time.Weekday(day), hour, personday.location)
						}
					}
				}
			}
		}
	}
	return s
}

func infoline(who string, weekday time.Weekday, hour int, location string) string {
	return fmt.Sprintf("%s on %s hour that starts at %d at %s\n", who, weekday, hour, location)
}

func (pp *PeriodPlan) String() string {
	s := fmt.Sprintf("From %d, month %d\n", pp.year, pp.fromMonth)
	s += fmt.Sprintf("Up to %d, month %d\n", pp.year, pp.uptoMonth)
	s += pp.ForAllWeekdays(infoline)
	return s
}

func (pp *PeriodPlan) ViewHour(t time.Time) string {
	s := ""

	//PeriodPlan
	//year        int
	//fromMonth   int
	//uptoMonth   int
	//personPlans []*PersonPlan

	// if not the right year
	if t.Year() != pp.year {
		return ""
	}

	// if not within the month range
	if !((t.Month() >= time.Month(pp.fromMonth)) && (t.Month() < time.Month(pp.uptoMonth))) {
		return ""
	}

	for _, persplan := range pp.personPlans {
		// PersonPlan
		//who      string
		//workdays []*WorkDayAndLocation
		for _, wd := range persplan.workdays {
			// WorkDayAndLocation
			// dayoftheweek time.Weekday
			// fromHour     int
			// uptoHour     int
			// location     string

			//fmt.Printf("persplan %d, workday %d\n", i1, i2)

			// If not the right day of the week
			if wd.dayoftheweek != t.Weekday() {
				//fmt.Printf("Wrong day of the week! (%v and %v)\n", wd.dayoftheweek, t.Weekday())
				continue
			}

			// If not within the hour range
			if !((t.Hour() >= wd.fromHour) && (t.Hour() < wd.uptoHour)) {
				//fmt.Printf("Wrong hour range! (%v is not between %v and %v)\n", t.Hour(), wd.fromHour, wd.uptoHour)
				continue
			}

			// Found!
			s += fmt.Sprintf("%s %s at %s, %v at hour %v\n", t.String()[:10], persplan.who, wd.location, t.Weekday(), t.Hour())
		}
	}

	return s
}

func (pp *PeriodPlan) ViewDay(date time.Time) string {
	var t time.Time
	var hourString string
	s := ""
	for hour := FIRSTHOUR; hour <= LASTHOUR; hour++ {
		//fmt.Printf("hour: %d\n", hour)
		t = time.Date(date.Year(), date.Month(), date.Day(), hour, 0, 0, 0, time.UTC)
		hourString = pp.ViewHour(t)
		if hourString != "" {
			s += hourString + "\n"
		}
	}
	return s
}

// Make new plans, which is a collection of PeriodPlans
func NewPlans() *Plans {
	var plans Plans
	plans.all = make([]*PeriodPlan, 0)
	return &plans
}

// Add a PeriodPlan to the collection of plans
func (plans *Plans) AddPeriodPlan(pp *PeriodPlan) {
	plans.all = append(plans.all, pp)
}

// TODO: Create a function just like this that returns an HourInfo type instead of strings
func (plans *Plans) HourInfo(date time.Time) {
	fmt.Printf("What's up at %s?\n", date.String())
	s := ""
	for _, pp := range plans.all {
		s += pp.ViewHour(date)
	}
	if s == "" {
		fmt.Println("Nothing!")
	} else {
		fmt.Println(s)
	}
}

func main() {
	ppAlexander := NewPersonPlan("Alexander")
	ppAlexander.AddWorkday(time.Monday, 8, 15, "KNH")     // monday, from 8, up to 15
	ppAlexander.AddWorkday(time.Wednesday, 12, 17, "KOH") // wednesday, from 12, up to 17

	fmt.Println(ppAlexander.String())

	ppBob := NewPersonPlan("Bob")
	ppBob.AddWorkday(time.Monday, 9, 11, "KOH")  // monday, from 9, up to 11
	ppBob.AddWorkday(time.Thursday, 8, 10, "KNH") // wednesday, from 8, up to 10

	fmt.Println(ppBob.String())

	periodplan := NewPeriodPlan(2013, 1, 8)
	periodplan.AddPersonPlan(ppAlexander)
	periodplan.AddPersonPlan(ppBob)

	fmt.Println(periodplan.String())

	allPlans := NewPlans()
	allPlans.AddPeriodPlan(periodplan)

	fmt.Println("Info for all plans:")
	date := time.Date(2013, 3, 4, 10, 32, 0, 0, time.UTC)

	for i, pp := range allPlans.all {
		fmt.Printf("Plan %d\n", i)
		fmt.Println(pp.ViewDay(date))
	}

	allPlans.HourInfo(date)

	allPlans.HourInfo(time.Date(2013, 3, 7, 9, 14, 0, 0, time.UTC))
}

<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/nf/stat"
	"http"
	"io/ioutil"
	"log"
	"rand"
	"time"
)

var (
	host = flag.String("host", "localhost:8080", "target host:port")
)

const (
	fooUrl = "http://example.net/foobar"
	monDelay  = 1e9
	getDelay  = 50e6
	postDelay = 50e6
	getters   = 100
	posters   = 10
)

var (
	newURL  = make(chan string)
	randURL = make(chan string)
)

func keeper() {
	var urls []string
	urls = append(urls, <-newURL)
	for {
		r := urls[rand.Intn(len(urls))]
		select {
		case u := <-newURL:
			urls = append(urls, u)
		case randURL <- r:
		}
	}
}

func post() {
	url := fmt.Sprintf("http://%s/add", *host)
	r, err := http.PostForm(url, map[string]string{"url": fooUrl})
	if err != nil {
		log.Println("post:", err)
		return
	}
	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Println("post:", err)
		return
	}
	newURL <- string(b)
	stat.In <- "post"
}

func get() {
	url := <-randURL
	r, err := http.Head(url)
	if err != nil {
		log.Println("get:", err)
		return
	}
	defer r.Body.Close()
	if r.StatusCode != 302 {
		log.Println("get: wrong StatusCode:", r.StatusCode)
	}
	if l := r.Header["Location"]; l != fooUrl {
		log.Println("get: wrong Location:", l)
	}
	stat.In <- "get"
}

func loop(fn func(), delay int64) {
	for {
		fn()
		time.Sleep(getDelay)
	}
}

func main() {
	flag.Parse()
	rand.Seed(time.Nanoseconds())
	go keeper()
	for i := 0; i < getters; i++ {
		go loop(get, getDelay)
	}
	for i := 0; i < posters; i++ {
		go loop(post, postDelay)
	}
	stat.Monitor(monDelay)
}
<commit_msg>support for multiple front-ends<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/nf/stat"
	"http"
	"io/ioutil"
	"log"
	"rand"
	"regexp"
	"strings"
	"time"
)

var (
	n      = flag.Int("n", 10, "magnitude of assault")
	host   = flag.String("host", "localhost:8080", "target host:port")
	hosts  []string
	hostRe = regexp.MustCompile("http://[a-zA-Z0-9:.]+")
)

const (
	fooUrl    = "http://example.net/foobar"
	monDelay  = 1e9
	getDelay  = 100e6
	getters   = 10
	postDelay = 100e6
	posters   = 1
)

var (
	newURL  = make(chan string)
	randURL = make(chan string)
)

func keeper() {
	var urls []string
	urls = append(urls, <-newURL)
	for {
		r := urls[rand.Intn(len(urls))]
		select {
		case u := <-newURL:
			for _, h := range hosts {
				u = hostRe.ReplaceAllString(u, "http://"+h)
				urls = append(urls, u)
			}
		case randURL <- r:
		}
	}
}

func post() {
	url := fmt.Sprintf("http://%s/add", hosts[rand.Intn(len(hosts))])
	r, err := http.PostForm(url, map[string]string{"url": fooUrl})
	if err != nil {
		log.Println("post:", err)
		return
	}
	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Println("post:", err)
		return
	}
	newURL <- string(b)
	stat.In <- "post"
}

func get() {
	url := <-randURL
	r, err := http.Head(url)
	if err != nil {
		log.Println("get:", err)
		return
	}
	defer r.Body.Close()
	if _, err := ioutil.ReadAll(r.Body); err != nil {
		log.Println("get:", err)
		return
	}
	if r.StatusCode != 302 {
		log.Println("get: wrong StatusCode:", r.StatusCode)
	}
	if l := r.Header["Location"]; l != fooUrl {
		log.Println("get: wrong Location:", l)
	}
	stat.In <- "get"
}

func loop(fn func(), delay int64) {
	for {
		fn()
		time.Sleep(getDelay)
	}
}

func main() {
	flag.Parse()
	hosts = strings.Split(*host, ",", -1)
	rand.Seed(time.Nanoseconds())
	go keeper()
	for i := 0; i < getters*(*n); i++ {
		go loop(get, getDelay)
	}
	for i := 0; i < posters*(*n); i++ {
		go loop(post, postDelay)
	}
	stat.Monitor(monDelay)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package rbac

import (
	"strings"

	"k8s.io/kubernetes/pkg/api/unversioned"
)

func RoleRefGroupKind(roleRef RoleRef) unversioned.GroupKind {
	return unversioned.GroupKind{Group: roleRef.APIGroup, Kind: roleRef.Kind}
}

func VerbMatches(rule PolicyRule, requestedVerb string) bool {
	for _, ruleVerb := range rule.Verbs {
		if ruleVerb == VerbAll {
			return true
		}
		if ruleVerb == requestedVerb {
			return true
		}
	}

	return false
}

func APIGroupMatches(rule PolicyRule, requestedGroup string) bool {
	for _, ruleGroup := range rule.APIGroups {
		if ruleGroup == APIGroupAll {
			return true
		}
		if ruleGroup == requestedGroup {
			return true
		}
	}

	return false
}

func ResourceMatches(rule PolicyRule, requestedResource string) bool {
	for _, ruleResource := range rule.Resources {
		if ruleResource == ResourceAll {
			return true
		}
		if ruleResource == requestedResource {
			return true
		}
	}

	return false
}

func ResourceNameMatches(rule PolicyRule, requestedName string) bool {
	if len(rule.ResourceNames) == 0 {
		return true
	}

	for _, ruleName := range rule.ResourceNames {
		if ruleName == requestedName {
			return true
		}
	}

	return false
}

func NonResourceURLMatches(rule PolicyRule, requestedURL string) bool {
	for _, ruleURL := range rule.NonResourceURLs {
		if ruleURL == NonResourceAll {
			return true
		}
		if ruleURL == requestedURL {
			return true
		}
		if strings.HasSuffix(ruleURL, "*") && strings.HasPrefix(requestedURL, strings.TrimRight(ruleURL, "*")) {
			return true
		}
	}

	return false
}
<commit_msg>start creating controller SA roles.  start with just one<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package rbac

import (
	"fmt"
	"strings"

	"k8s.io/kubernetes/pkg/api/unversioned"
)

func RoleRefGroupKind(roleRef RoleRef) unversioned.GroupKind {
	return unversioned.GroupKind{Group: roleRef.APIGroup, Kind: roleRef.Kind}
}

func VerbMatches(rule PolicyRule, requestedVerb string) bool {
	for _, ruleVerb := range rule.Verbs {
		if ruleVerb == VerbAll {
			return true
		}
		if ruleVerb == requestedVerb {
			return true
		}
	}

	return false
}

func APIGroupMatches(rule PolicyRule, requestedGroup string) bool {
	for _, ruleGroup := range rule.APIGroups {
		if ruleGroup == APIGroupAll {
			return true
		}
		if ruleGroup == requestedGroup {
			return true
		}
	}

	return false
}

func ResourceMatches(rule PolicyRule, requestedResource string) bool {
	for _, ruleResource := range rule.Resources {
		if ruleResource == ResourceAll {
			return true
		}
		if ruleResource == requestedResource {
			return true
		}
	}

	return false
}

func ResourceNameMatches(rule PolicyRule, requestedName string) bool {
	if len(rule.ResourceNames) == 0 {
		return true
	}

	for _, ruleName := range rule.ResourceNames {
		if ruleName == requestedName {
			return true
		}
	}

	return false
}

func NonResourceURLMatches(rule PolicyRule, requestedURL string) bool {
	for _, ruleURL := range rule.NonResourceURLs {
		if ruleURL == NonResourceAll {
			return true
		}
		if ruleURL == requestedURL {
			return true
		}
		if strings.HasSuffix(ruleURL, "*") && strings.HasPrefix(requestedURL, strings.TrimRight(ruleURL, "*")) {
			return true
		}
	}

	return false
}

// +k8s:deepcopy-gen=false
// PolicyRuleBuilder let's us attach methods.  A no-no for API types.
// We use it to construct rules in code.  It's more compact than trying to write them
// out in a literal and allows us to perform some basic checking during construction
type PolicyRuleBuilder struct {
	PolicyRule PolicyRule
}

func NewRule(verbs ...string) *PolicyRuleBuilder {
	return &PolicyRuleBuilder{
		PolicyRule: PolicyRule{Verbs: verbs},
	}
}

func (r *PolicyRuleBuilder) Groups(groups ...string) *PolicyRuleBuilder {
	r.PolicyRule.APIGroups = append(r.PolicyRule.APIGroups, groups...)
	return r
}

func (r *PolicyRuleBuilder) Resources(resources ...string) *PolicyRuleBuilder {
	r.PolicyRule.Resources = append(r.PolicyRule.Resources, resources...)
	return r
}

func (r *PolicyRuleBuilder) Names(names ...string) *PolicyRuleBuilder {
	r.PolicyRule.ResourceNames = append(r.PolicyRule.ResourceNames, names...)
	return r
}

func (r *PolicyRuleBuilder) URLs(urls ...string) *PolicyRuleBuilder {
	r.PolicyRule.NonResourceURLs = append(r.PolicyRule.NonResourceURLs, urls...)
	return r
}

func (r *PolicyRuleBuilder) RuleOrDie() PolicyRule {
	ret, err := r.Rule()
	if err != nil {
		panic(err)
	}
	return ret
}

func (r *PolicyRuleBuilder) Rule() (PolicyRule, error) {
	if len(r.PolicyRule.Verbs) == 0 {
		return PolicyRule{}, fmt.Errorf("verbs are required: %#v", r.PolicyRule)
	}

	switch {
	case len(r.PolicyRule.NonResourceURLs) > 0:
		if len(r.PolicyRule.APIGroups) != 0 || len(r.PolicyRule.Resources) != 0 || len(r.PolicyRule.ResourceNames) != 0 {
			return PolicyRule{}, fmt.Errorf("non-resource rule may not have apiGroups, resources, or resourceNames: %#v", r.PolicyRule)
		}
	case len(r.PolicyRule.Resources) > 0:
		if len(r.PolicyRule.NonResourceURLs) != 0 {
			return PolicyRule{}, fmt.Errorf("resource rule may not have nonResourceURLs: %#v", r.PolicyRule)
		}
		if len(r.PolicyRule.APIGroups) == 0 {
			// this a common bug
			return PolicyRule{}, fmt.Errorf("resource rule must have apiGroups: %#v", r.PolicyRule)
		}
	default:
		return PolicyRule{}, fmt.Errorf("a rule must have either nonResourceURLs or resources: %#v", r.PolicyRule)
	}

	return r.PolicyRule, nil
}
<|endoftext|>