<commit_before>package hpdf

/*
#cgo LDFLAGS: -lhpdf -lpng -lz
#include "hpdf.h"
*/
import "C"
import (
	"unsafe"
)

func (pdf *PDF) GetFont(fontName string, encodingName ...string) (*Font, error) {
	var cencodingName *C.char = nil

	cfontName := C.CString(fontName)
	if len(encodingName) > 0 {
		cencodingName = C.CString(encodingName[0])
	}
	font := C.HPDF_GetFont(pdf.doc, cfontName, cencodingName)
	C.free(unsafe.Pointer(cfontName))
	if cencodingName != nil {
		C.free(unsafe.Pointer(cencodingName))
	}

	if font == nil {
		return nil, pdf.GetLastError()
	}

	return newFont(font), pdf.GetLastError()
}

func (pdf *PDF) LoadType1FontFromFile(afmFn string, pfmFn ...string) (string, error) {
	var cpfmFn *C.char = nil

	cafmFn := C.CString(afmFn)
	if len(pfmFn) > 0 {
		cpfmFn = C.CString(pfmFn[0])
	}
	fontName := C.HPDF_LoadType1FontFromFile(pdf.doc, cafmFn, cpfmFn)
	C.free(unsafe.Pointer(cafmFn))
	if cpfmFn != nil {
		C.free(unsafe.Pointer(cpfmFn))
	}

	if fontName != nil {
		defer C.free(unsafe.Pointer(fontName))
		return C.GoString(fontName), nil
	} else {
		return "", pdf.GetLastError()
	}
}

func (pdf *PDF) LoadTTFontFromFile(fontName string, embedding bool) (string, error) {
	cfontName := C.CString(fontName)
	var cembedding C.HPDF_BOOL = C.HPDF_TRUE
	if !embedding {
		cembedding = C.HPDF_FALSE
	}
	retFontName := C.HPDF_LoadTTFontFromFile(pdf.doc, cfontName, cembedding)
	C.free(unsafe.Pointer(cfontName))

	if retFontName != nil {
		defer C.free(unsafe.Pointer(retFontName))
		return C.GoString(retFontName), nil
	} else {
		return "", pdf.GetLastError()
	}
}

func (pdf *PDF) LoadTTFontFromFile2(fontName string, index uint, embedding bool) (string, error) {
	cfontName := C.CString(fontName)
	var cembedding C.HPDF_BOOL = C.HPDF_TRUE
	if !embedding {
		cembedding = C.HPDF_FALSE
	}
	retFontName := C.HPDF_LoadTTFontFromFile2(pdf.doc, cfontName, C.HPDF_UINT(index), cembedding)
	C.free(unsafe.Pointer(cfontName))

	if retFontName != nil {
		defer C.free(unsafe.Pointer(retFontName))
		return C.GoString(retFontName), nil
	} else {
		return "", pdf.GetLastError()
	}
}

func (pdf *PDF) UseJPFonts() error {
	C.HPDF_UseJPFonts(pdf.doc)
	return pdf.GetLastError()
}

func (pdf *PDF) UseKRFonts() error {
	C.HPDF_UseKRFonts(pdf.doc)
	return pdf.GetLastError()
}

func (pdf *PDF) UseCNSFonts() error {
	C.HPDF_UseCNSFonts(pdf.doc)
	return pdf.GetLastError()
}

func (pdf *PDF) UseCNTFonts() error {
	C.HPDF_UseCNTFonts(pdf.doc)
	return pdf.GetLastError()
}

type Font struct {
	font C.HPDF_Font
}

func newFont(src C.HPDF_Font) *Font {
	font := &Font{src}
	return font
}

func (font *Font) GetFontName() string {
	cfontName := C.HPDF_Font_GetFontName(font.font)

	if cfontName != nil {
		return C.GoString(cfontName)
	} else {
		return ""
	}
}

func (font *Font) GetEncodingName() string {
	cencodingName := C.HPDF_Font_GetEncodingName(font.font)

	if cencodingName != nil {
		return C.GoString(cencodingName)
	} else {
		return ""
	}
}

func (font *Font) GetUnicodeWidth(char rune) int32 {
	ccharWidth := C.HPDF_Font_GetUnicodeWidth(font.font, C.HPDF_UNICODE(char))
	return int32(ccharWidth)
}

func (font *Font) GetBBox() *Box {
	return newBoxByHPDFBox(C.HPDF_Font_GetBBox(font.font))
}

func (font *Font) GetAscent() int32 {
	return int32(C.HPDF_Font_GetAscent(font.font))
}

func (font *Font) GetDescent() int32 {
	return int32(C.HPDF_Font_GetDescent(font.font))
}

func (font *Font) GetXHeight() uint32 {
	return uint32(C.HPDF_Font_GetXHeight(font.font))
}

func (font *Font) GetCapHeight() uint32 {
	return uint32(C.HPDF_Font_GetCapHeight(font.font))
}
<commit_msg>Fix failing test<commit_after>package hpdf

/*
#cgo LDFLAGS: -lhpdf -lpng -lz
#include "hpdf.h"
*/
import "C"
import (
	"unsafe"
)

func (pdf *PDF) GetFont(fontName string, encodingName ...string) (*Font, error) {
	var cencodingName *C.char = nil

	cfontName := C.CString(fontName)
	if len(encodingName) > 0 {
		cencodingName = C.CString(encodingName[0])
	}
	font := C.HPDF_GetFont(pdf.doc, cfontName, cencodingName)
	C.free(unsafe.Pointer(cfontName))
	if cencodingName != nil {
		C.free(unsafe.Pointer(cencodingName))
	}

	if font == nil {
		return nil, pdf.GetLastError()
	}

	return newFont(font), pdf.GetLastError()
}

func (pdf *PDF) LoadType1FontFromFile(afmFn string, pfmFn ...string) (string, error) {
	var cpfmFn *C.char = nil

	cafmFn := C.CString(afmFn)
	if len(pfmFn) > 0 {
		cpfmFn = C.CString(pfmFn[0])
	}
	fontName := C.HPDF_LoadType1FontFromFile(pdf.doc, cafmFn, cpfmFn)
	C.free(unsafe.Pointer(cafmFn))
	if cpfmFn != nil {
		C.free(unsafe.Pointer(cpfmFn))
	}

	if fontName != nil {
		return C.GoString(fontName), nil
	} else {
		return "", pdf.GetLastError()
	}
}

func (pdf *PDF) LoadTTFontFromFile(fontName string, embedding bool) (string, error) {
	cfontName := C.CString(fontName)
	var cembedding C.HPDF_BOOL = C.HPDF_TRUE
	if !embedding {
		cembedding = C.HPDF_FALSE
	}
	retFontName := C.HPDF_LoadTTFontFromFile(pdf.doc, cfontName, cembedding)
	C.free(unsafe.Pointer(cfontName))

	if retFontName != nil {
		return C.GoString(retFontName), nil
	} else {
		return "", pdf.GetLastError()
	}
}

func (pdf *PDF) LoadTTFontFromFile2(fontName string, index uint, embedding bool) (string, error) {
	cfontName := C.CString(fontName)
	var cembedding C.HPDF_BOOL = C.HPDF_TRUE
	if !embedding {
		cembedding = C.HPDF_FALSE
	}
	retFontName := C.HPDF_LoadTTFontFromFile2(pdf.doc, cfontName, C.HPDF_UINT(index), cembedding)
	C.free(unsafe.Pointer(cfontName))

	if retFontName != nil {
		return C.GoString(retFontName), nil
	} else {
		return "", pdf.GetLastError()
	}
}

func (pdf *PDF) UseJPFonts() error {
	C.HPDF_UseJPFonts(pdf.doc)
	return pdf.GetLastError()
}

func (pdf *PDF) UseKRFonts() error {
	C.HPDF_UseKRFonts(pdf.doc)
	return pdf.GetLastError()
}

func (pdf *PDF) UseCNSFonts() error {
	C.HPDF_UseCNSFonts(pdf.doc)
	return pdf.GetLastError()
}

func (pdf *PDF) UseCNTFonts() error {
	C.HPDF_UseCNTFonts(pdf.doc)
	return pdf.GetLastError()
}

type Font struct {
	font C.HPDF_Font
}

func newFont(src C.HPDF_Font) *Font {
	font := &Font{src}
	return font
}

func (font *Font) GetFontName() string {
	cfontName := C.HPDF_Font_GetFontName(font.font)

	if cfontName != nil {
		return C.GoString(cfontName)
	} else {
		return ""
	}
}

func (font *Font) GetEncodingName() string {
	cencodingName := C.HPDF_Font_GetEncodingName(font.font)

	if cencodingName != nil {
		return C.GoString(cencodingName)
	} else {
		return ""
	}
}

func (font *Font) GetUnicodeWidth(char rune) int32 {
	ccharWidth := C.HPDF_Font_GetUnicodeWidth(font.font, C.HPDF_UNICODE(char))
	return int32(ccharWidth)
}

func (font *Font) GetBBox() *Box {
	return newBoxByHPDFBox(C.HPDF_Font_GetBBox(font.font))
}

func (font *Font) GetAscent() int32 {
	return int32(C.HPDF_Font_GetAscent(font.font))
}

func (font *Font) GetDescent() int32 {
	return int32(C.HPDF_Font_GetDescent(font.font))
}

func (font *Font) GetXHeight() uint32 {
	return uint32(C.HPDF_Font_GetXHeight(font.font))
}

func (font *Font) GetCapHeight() uint32 {
	return uint32(C.HPDF_Font_GetCapHeight(font.font))
}
<|endoftext|>


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

// +build !plan9,!solaris

package main

import (
	"errors"
	"flag"
	"hash/fnv"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"syscall"

	"gopkg.in/fsnotify.v1"
	"gopkg.in/yaml.v2"
)

var (
	rules        []*rule
	config       *configuration
	logFile      *os.File
	matches      map[int64]*match
	matchesMu    = &sync.RWMutex{}
	errNoRules   = errors.New("no rules")
	errEmptyRule = errors.New("empty rule")
	configFile   = flag.String("config", ".gawp", "Configuration file")
	hasher64     = fnv.New64a()
	hasher64Mu   = &sync.Mutex{}
)

// Gawp configuration
type configuration struct {
	Recursive bool
	Workers   int
	Logfile   string
	Events    events
	Rules     map[string][]string
}

type rule struct {
	mu    *sync.Mutex
	match *regexp.Regexp
	cmds  []string
}

type match struct {
	rule *rule
	cmds [][]string
}

type events map[fsnotify.Op]struct{} // Emulate a "set"

// Checks if the event exists in the events set
func (e events) contains(op fsnotify.Op) bool {
	_, exists := e[op]

	return exists
}

// UnmarshalYAML unmarshals the string array
// into a event "set" for fast loopup
func (e *events) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var (
		events []string
		err    = unmarshal(&events)
	)

	if err != nil {
		return err
	} else if len(events) == 0 {
		return nil
	}

	*e = map[fsnotify.Op]struct{}{}

	for _, c := range events {
		switch strings.ToLower(c) {
		case "create":
			(*e)[fsnotify.Create] = struct{}{}
		case "write":
			(*e)[fsnotify.Write] = struct{}{}
		case "rename":
			(*e)[fsnotify.Rename] = struct{}{}
		case "remove":
			(*e)[fsnotify.Remove] = struct{}{}
		case "chmod":
			(*e)[fsnotify.Chmod] = struct{}{}
		}
	}

	return nil
}

func main() {
	flag.Parse()

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))

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

	defer logFile.Close()

	if rules, err = load(dir, *configFile); err != nil {
		log.Fatalf("Unable to load '%s' configuration file: %s", *configFile, err)
	}

	log.Printf("Loaded %d rules", len(rules))

	// File system notifications
	watcher, err := fsnotify.NewWatcher()

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

	defer watcher.Close()

	if config.Recursive {
		c := 0

		// Watch root and child directories
		filepath.Walk(dir+"/", func(path string, f os.FileInfo, err error) error {
			if !f.IsDir() {
				return nil
			}

			if n := path[len(dir):]; n != "/" && n[1] == '.' {
				return nil
			}

			if err := watcher.Add(path); err != nil {
				log.Printf("unable to watch path: %s (%s)", path, err)
			}

			c++

			return nil
		})

		log.Printf("watching %d directories", c)
	} else if err = watcher.Add(dir); err != nil {
		//  Only watch the root dir
		log.Fatal(err)
	}

	// Disable file system notifications for the log file
	if config.Logfile != "" {
		watcher.Remove(config.Logfile)
	}

	// We should always be able to run atleast 1 job
	if config.Workers < 1 {
		config.Workers = 1
	}

	var (
		filename string                                // Current filename path
		signals  = make(chan os.Signal, 2)             // OS signal capture
		throttle = make(chan struct{}, config.Workers) // Worker throttle
		wg       = &sync.WaitGroup{}
	)

	// Handle signals for clean shutdown
	signal.Notify(signals, os.Interrupt, syscall.SIGTERM)

	// Wait for workers on shutdown
	defer wg.Wait()

	for {
		select {
		case event := <-watcher.Events:
			if !config.Events.contains(event.Op) {
				continue
			}

			throttle <- struct{}{}

			// Reload config file
			if filename = event.Name[len(dir)+1:]; filename == *configFile {
				// Wait for active workers
				wg.Wait()

				log.Println("reloading config file")

				if rules, err = load(dir, filename); err != nil {
					log.Fatal(err)
				}

				log.Printf("loaded %d rules", len(rules))

				<-throttle

				continue
			}

			wg.Add(1)

			go worker(throttle, wg, filename)

		case err = <-watcher.Errors:
			log.Println("fsnotify error:", err)

		case <-signals:
			return
		}
	}
}

func worker(throttle chan struct{}, wg *sync.WaitGroup, f string) {
	defer func() {
		<-throttle

		wg.Done()
	}()

	m := findMatch(f)

	if m == nil {
		return
	}

	// Atomicity for the given rule
	m.rule.mu.Lock()

	defer m.rule.mu.Unlock()

	var cmd *exec.Cmd

	for i, c := range m.cmds {
		if len(c) == 1 {
			cmd = exec.Command(c[0])
		} else {
			cmd = exec.Command(c[0], c[1:]...)
		}

		if b, err := cmd.Output(); err != nil {
			log.Printf("Command (%s) error: %s", c, err)
		} else if len(b) > 0 {
			log.Printf("%s\n%s", m.rule.cmds[i], b)
		}
	}
}

// findMatch attempts to find a rule match for file path
// On success caches the match for fast future lookups
func findMatch(f string) *match {
	h := hash64(f)

	// Fast map lookup, circumvent regular expressions
	matchesMu.RLock()
	c, exists := matches[h]
	matchesMu.RUnlock()

	if exists {
		return c
	}

	// Test each rules for a match
	for _, r := range rules {
		m := r.match.FindAllStringSubmatch(f, -1)

		if m == nil {
			continue
		}

		v := &match{rule: r}

		for _, cmd := range r.cmds {
			for i := range m[0] {
				if i == 0 {
					continue
				}

				cmd = strings.Replace(cmd, "$"+strconv.Itoa(i), m[0][i], -1)
			}

			v.cmds = append(v.cmds, strings.Fields(strings.Replace(cmd, "$file", f, -1)))
		}

		// Cache for fast lookup
		matchesMu.Lock()
		matches[h] = v
		matchesMu.Unlock()

		return v
	}

	return nil
}

// loads the Gawp config file and handles the loading of rules
func load(dir string, f string) ([]*rule, error) {
	h, err := os.Open(dir + "/" + f)

	if err != nil {
		return nil, err
	}

	defer h.Close()

	b, err := ioutil.ReadAll(h)

	if err != nil {
		return nil, err
	}

	if err = yaml.Unmarshal(b, &config); err != nil {
		return nil, err
	}

	// Set log output
	if config.Logfile != "" {
		if config.Logfile[0] != '/' {
			config.Logfile = dir + "/" + config.Logfile
		}

		// No side effects
		logFile.Close()

		if logFile, err = os.OpenFile(config.Logfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666); err != nil {
			log.Fatal(err)
		}

		log.SetOutput(logFile)
	}

	if len(config.Rules) == 0 {
		return nil, errNoRules
	}

	var rules []*rule

	for k, v := range config.Rules {
		r := &rule{cmds: v, mu: &sync.Mutex{}}

		if r.match, err = regexp.Compile(k); err != nil {
			log.Println("Rule error:", k)

			continue
		}

		rules = append(rules, r)
	}

	// Init/reset matches cache
	matches = map[int64]*match{}

	return rules, nil
}

// hash64 returns the hash of the given string as int64
func hash64(s string) int64 {
	hasher64Mu.Lock()

	defer hasher64Mu.Unlock()

	hasher64.Reset()
	hasher64.Write([]byte(s))

	return int64(hasher64.Sum64())
}
<commit_msg>- Minor code improvements - Comment grammar - Lowercase error messages<commit_after>// Copyright Praegressus Limited. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !plan9,!solaris

package main

import (
	"errors"
	"flag"
	"hash/fnv"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"syscall"

	"gopkg.in/fsnotify.v1"
	"gopkg.in/yaml.v2"
)

var (
	rules        []*rule
	config       *configuration
	logFile      *os.File
	matches      map[int64]*match
	matchesMu    = &sync.RWMutex{}
	errNoRules   = errors.New("no rules")
	errEmptyRule = errors.New("empty rule")
	configFile   = flag.String("config", ".gawp", "Configuration file")
	hasher64     = fnv.New64a()
	hasher64Mu   = &sync.Mutex{}
)

// Gawp configuration
type configuration struct {
	Recursive bool
	Workers   int
	Logfile   string
	Events    events
	Rules     map[string][]string
}

type rule struct {
	mu    *sync.Mutex
	match *regexp.Regexp
	cmds  []string
}

type match struct {
	rule *rule
	cmds [][]string
}

type events map[fsnotify.Op]struct{} // Emulate a "set"

// Checks if the event exists in the events set
func (e events) contains(op fsnotify.Op) bool {
	_, exists := e[op]

	return exists
}

// UnmarshalYAML unmarshals the string array
// into a event "set" for fast loopup
func (e *events) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var events []string

	if err := unmarshal(&events); err != nil {
		return err
	} else if len(events) == 0 {
		return nil
	}

	*e = map[fsnotify.Op]struct{}{}

	for _, c := range events {
		switch strings.ToLower(c) {
		case "create":
			(*e)[fsnotify.Create] = struct{}{}
		case "write":
			(*e)[fsnotify.Write] = struct{}{}
		case "rename":
			(*e)[fsnotify.Rename] = struct{}{}
		case "remove":
			(*e)[fsnotify.Remove] = struct{}{}
		case "chmod":
			(*e)[fsnotify.Chmod] = struct{}{}
		}
	}

	return nil
}

func main() {
	flag.Parse()

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))

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

	defer logFile.Close()

	if rules, err = load(dir, *configFile); err != nil {
		log.Fatalf("unable to load configuration file: %s (%s)", *configFile, err)
	}

	log.Printf("loaded %d rules", len(rules))

	// File system notifications
	watcher, err := fsnotify.NewWatcher()

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

	defer watcher.Close()

	if config.Recursive {
		c := 0

		// Watch root and child directories
		filepath.Walk(dir+"/", func(path string, f os.FileInfo, err error) error {
			if !f.IsDir() {
				return nil
			}

			if n := path[len(dir):]; n != "/" && n[1] == '.' {
				return nil
			}

			if err := watcher.Add(path); err != nil {
				log.Printf("unable to watch path: %s (%s)", path, err)
			}

			c++

			return nil
		})

		log.Printf("watching %d directories", c)
	} else if err = watcher.Add(dir); err != nil {
		//  Only watch the root dir
		log.Fatal(err)
	}

	// Disable file system notifications for the log file
	if config.Logfile != "" {
		watcher.Remove(config.Logfile)
	}

	// We should always be able to run atleast 1 job
	if config.Workers < 1 {
		config.Workers = 1
	}

	var (
		filename string                                // Current filename path
		signals  = make(chan os.Signal, 2)             // OS signal capture
		throttle = make(chan struct{}, config.Workers) // Worker throttle
		wg       = &sync.WaitGroup{}
	)

	// Handle signals for clean shutdown
	signal.Notify(signals, os.Interrupt, syscall.SIGTERM)

	// Wait for workers on shutdown
	defer wg.Wait()

	for {
		select {
		case event := <-watcher.Events:
			if !config.Events.contains(event.Op) {
				continue
			}

			throttle <- struct{}{}

			// Reload config file
			if filename = event.Name[len(dir)+1:]; filename == *configFile {
				// Wait for active workers
				wg.Wait()

				log.Println("reloading config file")

				if rules, err = load(dir, filename); err != nil {
					log.Fatal(err)
				}

				log.Printf("loaded %d rules", len(rules))

				<-throttle

				continue
			}

			wg.Add(1)

			go worker(throttle, wg, filename)

		case err = <-watcher.Errors:
			log.Println("fsnotify error:", err)

		case <-signals:
			return
		}
	}
}

func worker(throttle chan struct{}, wg *sync.WaitGroup, f string) {
	defer func() {
		<-throttle

		wg.Done()
	}()

	m := findMatch(f)

	if m == nil {
		return
	}

	// Atomicity for the given rule
	m.rule.mu.Lock()

	defer m.rule.mu.Unlock()

	var cmd *exec.Cmd

	for i, c := range m.cmds {
		if len(c) == 1 {
			cmd = exec.Command(c[0])
		} else {
			cmd = exec.Command(c[0], c[1:]...)
		}

		if b, err := cmd.Output(); err != nil {
			log.Printf("command (%s) error: %s", c, err)
		} else if len(b) > 0 {
			log.Printf("%s\n%s", m.rule.cmds[i], b)
		}
	}
}

// findMatch attempts to find a rule match for file path
// On success caches the match for fast future lookups
func findMatch(f string) *match {
	h := hash64(f)

	// Fast map lookup, circumvent regular expressions
	matchesMu.RLock()
	c, exists := matches[h]
	matchesMu.RUnlock()

	if exists {
		return c
	}

	// Test each rule for a match
	for _, r := range rules {
		m := r.match.FindAllStringSubmatch(f, -1)

		if m == nil {
			continue
		}

		v := &match{rule: r}

		for _, cmd := range r.cmds {
			for i := range m[0] {
				if i == 0 {
					continue
				}

				cmd = strings.Replace(cmd, "$"+strconv.Itoa(i), m[0][i], -1)
			}

			v.cmds = append(v.cmds, strings.Fields(strings.Replace(cmd, "$file", f, -1)))
		}

		// Cache for fast lookup
		matchesMu.Lock()
		matches[h] = v
		matchesMu.Unlock()

		return v
	}

	return nil
}

// loads the Gawp config file and handles the loading of rules
func load(dir string, f string) ([]*rule, error) {
	h, err := os.Open(dir + "/" + f)

	if err != nil {
		return nil, err
	}

	defer h.Close()

	b, err := ioutil.ReadAll(h)

	if err != nil {
		return nil, err
	}

	// Overwrite config
	config = nil

	if err = yaml.Unmarshal(b, &config); err != nil {
		return nil, err
	}

	// Set log output
	if config.Logfile != "" {
		if config.Logfile[0] != '/' {
			config.Logfile = dir + "/" + config.Logfile
		}

		// No side effects
		logFile.Close()

		if logFile, err = os.OpenFile(config.Logfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666); err != nil {
			log.Fatal(err)
		}

		log.SetOutput(logFile)
	}

	if len(config.Rules) == 0 {
		return nil, errNoRules
	}

	var rules []*rule

	for k, v := range config.Rules {
		r := &rule{cmds: v, mu: &sync.Mutex{}}

		if r.match, err = regexp.Compile(k); err != nil {
			log.Println("rule error:", k)

			continue
		}

		rules = append(rules, r)
	}

	// Init/reset matches cache
	matches = map[int64]*match{}

	return rules, nil
}

// hash64 returns the hash of the given string as int64
func hash64(s string) int64 {
	hasher64Mu.Lock()

	defer hasher64Mu.Unlock()

	hasher64.Reset()
	hasher64.Write([]byte(s))

	return int64(hasher64.Sum64())
}
<|endoftext|>


<commit_before>//
//
//
//

package gdbm

// #cgo CFLAGS: -std=gnu99
// #cgo LDFLAGS: -lgdbm
// #include <stdlib.h>
// #include <gdbm.h>
// #include <string.h>
// inline datum mk_datum(char * s) {
//     datum d;
//     d.dptr = s;
//     d.dsize = strlen(s);
//     return d;
// }
import "C"

import (
    "errors"
    "unsafe"
)

//
type Database struct {
    dbf C.GDBM_FILE
}

//
type DatabaseCfg struct {
    Mode string
    BlockSize int
    Permissions int
}

func lastError() error {
    return errors.New(C.GoString(C.gdbm_strerror(C.gdbm_errno)))
}

// Simple function to open a database file with default parameters (block size
// is default for the filesystem and file permissions are set to 0666).
func Open(filename string, mode string) (db * Database, err error) {
    return OpenWithCfg(filename, DatabaseCfg{mode, 0, 0666})
}

func OpenWithCfg(filename string, cfg DatabaseCfg) (db * Database, err error) {
    var m int
    switch cfg.Mode {
    case "r": m = C.GDBM_READER
    case "w": m = C.GDBM_WRITER
    case "c": m = C.GDBM_WRCREAT
    case "n": m = C.GDBM_NEWDB
    }

    cs := C.CString(filename)
    defer C.free(unsafe.Pointer(cs))

    db.dbf = C.gdbm_open(cs, C.int(cfg.BlockSize), C.int(m), C.int(cfg.Permissions), nil)
    if db.dbf == nil {
        err = lastError()
    }
    return db, err
}

// Closes a database file.
func (db * Database) Close() {
    C.gdbm_close(db.dbf)
}

//
func (db * Database) update(key string, value string, flag C.int) (err error) {
    kcs := C.CString(key)
    vcs := C.CString(value)
    k := C.mk_datum(kcs)
    v := C.mk_datum(vcs)
    defer C.free(unsafe.Pointer(kcs))
    defer C.free(unsafe.Pointer(vcs))

    retv := C.gdbm_store(db.dbf, k, v, flag)
    if retv != 0 {
        err = lastError()
    }
    return err
}

//
func (db * Database) Insert(key string, value string) (err error) {
    return db.update(key, value, C.GDBM_INSERT)
}

//
func (db * Database) Replace(key string, value string) (err error) {
    return db.update(key, value, C.GDBM_REPLACE)
}

func (db * Database) Exists(key string) bool {
    kcs := C.CString(key)
    k := C.mk_datum(kcs)
    defer C.free(unsafe.Pointer(kcs))

    e := C.gdbm_exists(db.dbf, k)
    if e == 1 {
        return true
    }
    return false
}
<commit_msg>Add method skeletons<commit_after>//
//
//
//

package gdbm

// #cgo CFLAGS: -std=gnu99
// #cgo LDFLAGS: -lgdbm
// #include <stdlib.h>
// #include <gdbm.h>
// #include <string.h>
// inline datum mk_datum(char * s) {
//     datum d;
//     d.dptr = s;
//     d.dsize = strlen(s);
//     return d;
// }
import "C"

import (
    "errors"
    "unsafe"
)

//
type Database struct {
    dbf C.GDBM_FILE
}

//
type DatabaseCfg struct {
    Mode string
    BlockSize int
    Permissions int
}

func lastError() error {
    return errors.New(C.GoString(C.gdbm_strerror(C.gdbm_errno)))
}

// Simple function to open a database file with default parameters (block size
// is default for the filesystem and file permissions are set to 0666).
func Open(filename string, mode string) (db * Database, err error) {
    return OpenWithCfg(filename, DatabaseCfg{mode, 0, 0666})
}

func OpenWithCfg(filename string, cfg DatabaseCfg) (db * Database, err error) {
    var m int
    switch cfg.Mode {
    case "r": m = C.GDBM_READER
    case "w": m = C.GDBM_WRITER
    case "c": m = C.GDBM_WRCREAT
    case "n": m = C.GDBM_NEWDB
    }

    cs := C.CString(filename)
    defer C.free(unsafe.Pointer(cs))

    db.dbf = C.gdbm_open(cs, C.int(cfg.BlockSize), C.int(m), C.int(cfg.Permissions), nil)
    if db.dbf == nil {
        err = lastError()
    }
    return db, err
}

// Closes a database file.
func (db * Database) Close() {
    C.gdbm_close(db.dbf)
}

//
func (db * Database) update(key string, value string, flag C.int) (err error) {
    kcs := C.CString(key)
    vcs := C.CString(value)
    k := C.mk_datum(kcs)
    v := C.mk_datum(vcs)
    defer C.free(unsafe.Pointer(kcs))
    defer C.free(unsafe.Pointer(vcs))

    retv := C.gdbm_store(db.dbf, k, v, flag)
    if retv != 0 {
        err = lastError()
    }
    return err
}

//
func (db * Database) Insert(key string, value string) (err error) {
    return db.update(key, value, C.GDBM_INSERT)
}

//
func (db * Database) Replace(key string, value string) (err error) {
    return db.update(key, value, C.GDBM_REPLACE)
}

func (db * Database) Exists(key string) bool {
    kcs := C.CString(key)
    k := C.mk_datum(kcs)
    defer C.free(unsafe.Pointer(kcs))

    e := C.gdbm_exists(db.dbf, k)
    if e == 1 {
        return true
    }
    return false
}

func (db * Database) Fetch() {}

func (db * Database) Delete() {}

func (db * Database) Reorganize() {}

func (db * Database) Sync() {}
<|endoftext|>


<commit_before>package ghch

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/Songmu/gitsemvers"
	"github.com/octokit/go-octokit/octokit"
	"github.com/pkg/errors"
	"github.com/tcnksm/go-gitconfig"
)

const version = "0.0.4"

type ghch struct {
	repoPath string
	gitPath  string
	remote   string
	verbose  bool
	token    string
	baseURL  string
	client   *octokit.Client
}

func (gh *ghch) initialize() *ghch {
	var auth octokit.AuthMethod
	gh.setToken()
	if gh.token != "" {
		auth = octokit.TokenAuth{AccessToken: gh.token}
	}

	gh.setBaseURL()

	if gh.baseURL != "" {
		gh.client = octokit.NewClientWith(gh.baseURL, "Octokit Go", auth, nil)
		return gh
	}

	gh.client = octokit.NewClient(auth)
	return gh
}

func (gh *ghch) setToken() {
	if gh.token != "" {
		return
	}
	if gh.token = os.Getenv("GITHUB_TOKEN"); gh.token != "" {
		return
	}
	gh.token, _ = gitconfig.GithubToken()
	return
}

func (gh *ghch) setBaseURL() {
	if gh.baseURL != "" {
		return
	}
	if gh.baseURL = os.Getenv("GITHUB_API"); gh.baseURL != "" {
		return
	}

	return
}

func (gh *ghch) gitProg() string {
	if gh.gitPath != "" {
		return gh.gitPath
	}
	return "git"
}

func (gh *ghch) cmd(argv ...string) (string, error) {
	arg := []string{"-C", gh.repoPath}
	arg = append(arg, argv...)
	cmd := exec.Command(gh.gitProg(), arg...)
	cmd.Env = append(os.Environ(), "LANG=C")

	var b bytes.Buffer
	cmd.Stdout = &b
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	return b.String(), err
}

var verReg = regexp.MustCompile(`^v?[0-9]+(?:\.[0-9]+){0,2}$`)

func (gh *ghch) versions() []string {
	sv := gitsemvers.Semvers{
		RepoPath: gh.repoPath,
		GitPath:  gh.gitPath,
	}
	return sv.VersionStrings()
}

func (gh *ghch) getRemote() string {
	if gh.remote != "" {
		return gh.remote
	}
	return "origin"
}

var repoURLReg = regexp.MustCompile(`([^/:]+)/([^/]+?)(?:\.git)?$`)

func (gh *ghch) ownerAndRepo() (owner, repo string) {
	out, _ := gh.cmd("remote", "-v")
	remotes := strings.Split(out, "\n")
	for _, r := range remotes {
		fields := strings.Fields(r)
		if len(fields) > 1 && fields[0] == gh.getRemote() {
			if matches := repoURLReg.FindStringSubmatch(fields[1]); len(matches) > 2 {
				return matches[1], matches[2]
			}
		}
	}
	return
}

func (gh *ghch) mergedPRs(from, to string) (prs []*octokit.PullRequest) {
	owner, repo := gh.ownerAndRepo()
	prlogs := gh.mergedPRLogs(from, to)
	prs = make([]*octokit.PullRequest, 0, len(prlogs))
	prsWithNil := make([]*octokit.PullRequest, len(prlogs))

	var wg sync.WaitGroup

	for i, prlog := range prlogs {
		wg.Add(1)
		go func(i int, prlog *mergedPRLog) {
			defer wg.Done()
			num := prlog.num
			url, _ := octokit.PullRequestsURL.Expand(octokit.M{"owner": owner, "repo": repo, "number": num})
			pr, r := gh.client.PullRequests(url).One()
			if r.HasError() {
				log.Print(r.Err)
				return
			}
			if !gh.verbose {
				pr = reducePR(pr)
			}
			prsWithNil[i] = pr
		}(i, prlog)
	}
	wg.Wait()
	for _, pr := range prsWithNil {
		if pr != nil {
			prs = append(prs, pr)
		}
	}
	return
}

func (gh *ghch) getLatestSemverTag() string {
	vers := gh.versions()
	if len(vers) < 1 {
		return ""
	}
	return vers[0]
}

type mergedPRLog struct {
	num    int
	branch string
}

func (gh *ghch) mergedPRLogs(from, to string) (nums []*mergedPRLog) {
	if from == "" {
		from, _ = gh.cmd("rev-list", "--max-parents=0", "HEAD")
		from = strings.TrimSpace(from)
	}

	revisionRange := fmt.Sprintf("%s..%s", from, to)
	out, err := gh.cmd("log", revisionRange, "--merges", "--oneline")
	if err != nil {
		return
	}
	return parseMergedPRLogs(out)
}

var prMergeReg = regexp.MustCompile(`^[a-f0-9]{7} Merge pull request #([0-9]+) from (\S+)`)

func parseMergedPRLogs(out string) (prs []*mergedPRLog) {
	lines := strings.Split(out, "\n")
	for _, line := range lines {
		if matches := prMergeReg.FindStringSubmatch(line); len(matches) > 2 {
			i, _ := strconv.Atoi(matches[1])
			prs = append(prs, &mergedPRLog{
				num:    i,
				branch: matches[2],
			})
		}
	}
	return
}

func (gh *ghch) getChangedAt(rev string) (time.Time, error) {
	if rev == "" {
		rev = "HEAD"
	}
	out, err := gh.cmd("show", "-s", rev+"^{commit}", `--format=%ct`)
	if err != nil {
		return time.Time{}, errors.Wrap(err, "failed to get changed at from git revision. `git show` failed")
	}
	out = strings.TrimSpace(out)
	i, err := strconv.ParseInt(out, 10, 64)
	if err != nil {
		return time.Time{}, errors.Wrap(err, "failed to get changed at from git revision. ParseInt failed")
	}
	return time.Unix(i, 0), nil
}
<commit_msg>suppress error message<commit_after>package ghch

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/Songmu/gitsemvers"
	"github.com/octokit/go-octokit/octokit"
	"github.com/pkg/errors"
	"github.com/tcnksm/go-gitconfig"
)

const version = "0.0.4"

type ghch struct {
	repoPath string
	gitPath  string
	remote   string
	verbose  bool
	token    string
	baseURL  string
	client   *octokit.Client
}

func (gh *ghch) initialize() *ghch {
	var auth octokit.AuthMethod
	gh.setToken()
	if gh.token != "" {
		auth = octokit.TokenAuth{AccessToken: gh.token}
	}

	gh.setBaseURL()

	if gh.baseURL != "" {
		gh.client = octokit.NewClientWith(gh.baseURL, "Octokit Go", auth, nil)
		return gh
	}

	gh.client = octokit.NewClient(auth)
	return gh
}

func (gh *ghch) setToken() {
	if gh.token != "" {
		return
	}
	if gh.token = os.Getenv("GITHUB_TOKEN"); gh.token != "" {
		return
	}
	gh.token, _ = gitconfig.GithubToken()
	return
}

func (gh *ghch) setBaseURL() {
	if gh.baseURL != "" {
		return
	}
	if gh.baseURL = os.Getenv("GITHUB_API"); gh.baseURL != "" {
		return
	}

	return
}

func (gh *ghch) gitProg() string {
	if gh.gitPath != "" {
		return gh.gitPath
	}
	return "git"
}

func (gh *ghch) cmd(argv ...string) (string, error) {
	arg := []string{"-C", gh.repoPath}
	arg = append(arg, argv...)
	cmd := exec.Command(gh.gitProg(), arg...)
	cmd.Env = append(os.Environ(), "LANG=C")

	var b bytes.Buffer
	cmd.Stdout = &b
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	return b.String(), err
}

var verReg = regexp.MustCompile(`^v?[0-9]+(?:\.[0-9]+){0,2}$`)

func (gh *ghch) versions() []string {
	sv := gitsemvers.Semvers{
		RepoPath: gh.repoPath,
		GitPath:  gh.gitPath,
	}
	return sv.VersionStrings()
}

func (gh *ghch) getRemote() string {
	if gh.remote != "" {
		return gh.remote
	}
	return "origin"
}

var repoURLReg = regexp.MustCompile(`([^/:]+)/([^/]+?)(?:\.git)?$`)

func (gh *ghch) ownerAndRepo() (owner, repo string) {
	out, _ := gh.cmd("remote", "-v")
	remotes := strings.Split(out, "\n")
	for _, r := range remotes {
		fields := strings.Fields(r)
		if len(fields) > 1 && fields[0] == gh.getRemote() {
			if matches := repoURLReg.FindStringSubmatch(fields[1]); len(matches) > 2 {
				return matches[1], matches[2]
			}
		}
	}
	return
}

func (gh *ghch) mergedPRs(from, to string) (prs []*octokit.PullRequest) {
	owner, repo := gh.ownerAndRepo()
	prlogs := gh.mergedPRLogs(from, to)
	prs = make([]*octokit.PullRequest, 0, len(prlogs))
	prsWithNil := make([]*octokit.PullRequest, len(prlogs))

	var wg sync.WaitGroup

	for i, prlog := range prlogs {
		wg.Add(1)
		go func(i int, prlog *mergedPRLog) {
			defer wg.Done()
			num := prlog.num
			url, _ := octokit.PullRequestsURL.Expand(octokit.M{"owner": owner, "repo": repo, "number": num})
			pr, r := gh.client.PullRequests(url).One()
			if r.Err != nil {
				if rerr, ok := r.Err.(*octokit.ResponseError); ok {
					if rerr.Response != nil && rerr.Response.StatusCode == http.StatusNotFound {
						return
					}
				}
				log.Print(r.Err)
				return
			}
			if !gh.verbose {
				pr = reducePR(pr)
			}
			prsWithNil[i] = pr
		}(i, prlog)
	}
	wg.Wait()
	for _, pr := range prsWithNil {
		if pr != nil {
			prs = append(prs, pr)
		}
	}
	return
}

func (gh *ghch) getLatestSemverTag() string {
	vers := gh.versions()
	if len(vers) < 1 {
		return ""
	}
	return vers[0]
}

type mergedPRLog struct {
	num    int
	branch string
}

func (gh *ghch) mergedPRLogs(from, to string) (nums []*mergedPRLog) {
	if from == "" {
		from, _ = gh.cmd("rev-list", "--max-parents=0", "HEAD")
		from = strings.TrimSpace(from)
	}

	revisionRange := fmt.Sprintf("%s..%s", from, to)
	out, err := gh.cmd("log", revisionRange, "--merges", "--oneline")
	if err != nil {
		return
	}
	return parseMergedPRLogs(out)
}

var prMergeReg = regexp.MustCompile(`^[a-f0-9]{7} Merge pull request #([0-9]+) from (\S+)`)

func parseMergedPRLogs(out string) (prs []*mergedPRLog) {
	lines := strings.Split(out, "\n")
	for _, line := range lines {
		if matches := prMergeReg.FindStringSubmatch(line); len(matches) > 2 {
			i, _ := strconv.Atoi(matches[1])
			prs = append(prs, &mergedPRLog{
				num:    i,
				branch: matches[2],
			})
		}
	}
	return
}

func (gh *ghch) getChangedAt(rev string) (time.Time, error) {
	if rev == "" {
		rev = "HEAD"
	}
	out, err := gh.cmd("show", "-s", rev+"^{commit}", `--format=%ct`)
	if err != nil {
		return time.Time{}, errors.Wrap(err, "failed to get changed at from git revision. `git show` failed")
	}
	out = strings.TrimSpace(out)
	i, err := strconv.ParseInt(out, 10, 64)
	if err != nil {
		return time.Time{}, errors.Wrap(err, "failed to get changed at from git revision. ParseInt failed")
	}
	return time.Unix(i, 0), nil
}
<|endoftext|>


<commit_before>package heap

// FibHeap Fibonacci Heap structure
type FibHeap struct {
	count int
	min   *fibHeapNode
	comp  func(a, b interface{}) bool
}

// fibHeapNode represents node of FibHeap
type fibHeapNode struct {
	value  interface{}
	marked bool
	degree int
	left   *fibHeapNode
	right  *fibHeapNode
	parent *fibHeapNode
	child  *fibHeapNode
}

// NewFibHeap returns a pointer to FibHeap
func NewFibHeap(comp func(interface{}, interface{}) bool) *FibHeap {
	h := &FibHeap{
		comp: comp,
	}
	return h
}

// Size returns size of a heap
func (h *FibHeap) Size() int {
	return h.count
}

// IsEmpty returns true if heap is empty
func (h *FibHeap) IsEmpty() bool {
	return h.count == 0
}

// Push adds value to the heap in O(1) time
func (h *FibHeap) Push(value interface{}) {
	n := &fibHeapNode{
		value: value,
	}
	h.count++
	h.insert(n)
}

// Pop removes top element from the heap in O(log(N)) time
func (h *FibHeap) Pop() (value interface{}, ok bool) {
	if h.min == nil {
		return nil, false
	}
	minNode := h.min

	if minNode.child != nil {
		child := minNode.child
		for minNode == child.parent {
			nextChild := child.right
			h.insert(child)
			child = nextChild
		}
	}

	minNode.left.right = minNode.right
	minNode.right.left = minNode.left

	if minNode.right == minNode {
		h.min = nil
	} else {
		h.min = h.min.right
		h.consolidate()
	}
	h.count--

	return minNode.value, true
}

// Top returns top element from the heap in O(1) time
func (h *FibHeap) Top() (value interface{}, ok bool) {
	if h.min == nil {
		return nil, false
	}
	return h.min.value, true
}

// insert appends new node to the root list
func (h *FibHeap) insert(x *fibHeapNode) {
	x.parent = nil
	x.marked = false

	if h.min == nil {
		h.min = x
		h.min.right = h.min
		h.min.left = h.min
		return
	}

	x.left = h.min.left
	x.right = h.min
	x.left.right = x
	x.right.left = x

	if h.comp(x.value, h.min.value) {
		h.min = x
	}
}

// consolidate changes structure of the heap
func (h *FibHeap) consolidate() {
	nodes := make([]*fibHeapNode, h.count+1)

	rootNodes := make([]*fibHeapNode, 0)
	rootNodes = append(rootNodes, h.min)
	for n := h.min.right; n != h.min; n = n.right {
		rootNodes = append(rootNodes, n)
	}

	for _, node := range rootNodes {
		if node.parent != nil {
			continue
		}

		d := node.degree
		for nodes[d] != nil {
			y := nodes[d]
			if !h.comp(node.value, y.value) {
				node, y = y, node
			}
			h.link(y, node)
			nodes[d] = nil
			d++
		}
		nodes[d] = node
	}

	h.min = nil

	for _, node := range nodes {
		if node != nil {
			h.insert(node)
		}
	}
}

// link make y child of x
func (h *FibHeap) link(y, x *fibHeapNode) {
	y.left.right = y.right
	y.right.left = y.left

	if x.child == nil {
		y.right = y
		y.left = y
	} else {
		y.left = x.child.left
		y.right = x.child
		y.right.left = y
		y.left.right = y
	}
	x.child = y
	y.parent = x

	x.degree++
	y.marked = false
}
<commit_msg>heap: FibHeap code cleanup (#60)<commit_after>package heap

// FibHeap Fibonacci Heap structure
type FibHeap struct {
	count int
	min   *fibHeapNode
	comp  func(a, b interface{}) bool
}

// fibHeapNode represents node of FibHeap
type fibHeapNode struct {
	value  interface{}
	marked bool
	degree int
	left   *fibHeapNode
	right  *fibHeapNode
	parent *fibHeapNode
	child  *fibHeapNode
}

// NewFibHeap returns a pointer to FibHeap
func NewFibHeap(comp func(interface{}, interface{}) bool) *FibHeap {
	h := &FibHeap{
		comp: comp,
	}
	return h
}

// Size returns size of a heap
func (h *FibHeap) Size() int {
	return h.count
}

// IsEmpty returns true if heap is empty
func (h *FibHeap) IsEmpty() bool {
	return h.count == 0
}

// Push adds value to the heap in O(1) time
func (h *FibHeap) Push(value interface{}) {
	n := &fibHeapNode{
		value: value,
	}
	h.count++
	h.insert(n)
}

// Pop removes top element from the heap in O(log(N)) time
func (h *FibHeap) Pop() (value interface{}, ok bool) {
	if h.min == nil {
		return nil, false
	}
	minNode := h.min

	if minNode.child != nil {
		child := minNode.child
		for minNode == child.parent {
			nextChild := child.right
			h.insert(child)
			child = nextChild
		}
	}

	minNode.left.right = minNode.right
	minNode.right.left = minNode.left

	if minNode.right == minNode {
		h.min = nil
	} else {
		h.min = minNode.right
		h.consolidate()
	}
	h.count--

	return minNode.value, true
}

// Top returns top element from the heap in O(1) time
func (h *FibHeap) Top() (value interface{}, ok bool) {
	if h.min == nil {
		return nil, false
	}
	return h.min.value, true
}

// insert appends new node to the root list
func (h *FibHeap) insert(x *fibHeapNode) {
	x.parent = nil
	x.marked = false

	if h.min == nil {
		h.min = x
		h.min.left = h.min
		h.min.right = h.min
		return
	}

	x.left = h.min.left
	x.right = h.min
	x.left.right = x
	x.right.left = x

	if h.comp(x.value, h.min.value) {
		h.min = x
	}
}

// consolidate changes structure of the heap
func (h *FibHeap) consolidate() {
	nodes := make([]*fibHeapNode, h.count+1)

	rootNodes := make([]*fibHeapNode, 0)
	rootNodes = append(rootNodes, h.min)
	for n := h.min.right; n != h.min; n = n.right {
		rootNodes = append(rootNodes, n)
	}

	for _, node := range rootNodes {
		d := node.degree
		for nodes[d] != nil {
			y := nodes[d]
			if h.comp(y.value, node.value) {
				node, y = y, node
			}
			h.link(y, node)
			nodes[d] = nil
			d++
		}
		nodes[d] = node
	}

	h.min = nil

	for _, node := range nodes {
		if node != nil {
			h.insert(node)
		}
	}
}

// link make y child of x
func (h *FibHeap) link(y, x *fibHeapNode) {
	y.left.right = y.right
	y.right.left = y.left

	if x.child == nil {
		y.left = y
		y.right = y
	} else {
		y.left = x.child.left
		y.right = x.child
		y.left.right = y
		y.right.left = y
	}
	x.child = y
	y.parent = x

	x.degree++
	y.marked = false
}
<|endoftext|>


<commit_before>/****************************************************************************
 * Copyright (c) 2013, Scott Ferguson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the software nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

package goat

import (
	"encoding/gob"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/scottferg/gospdy"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"net"
	"net/http"
	"net/url"
	"strconv"
)

const (
	GET    = 1 << 0
	POST   = 1 << 1
	PUT    = 1 << 2
	DELETE = 1 << 3

	methodGet    = "GET"
	methodPost   = "POST"
	methodPut    = "PUT"
	methodDelete = "DELETE"
)

type Goat struct {
	Router       *mux.Router
	routes       map[string]*route
	middleware   []Middleware
	dbsession    *mgo.Session
	dbname       string
	sessionstore sessions.Store
	listener     *net.TCPListener
	servemux     *http.ServeMux
}

type Handler func(http.ResponseWriter, *http.Request, *Context) error

type route struct {
	*Goat
	path        string
	name        string
	handler     Handler
	interceptor Interceptor
}

func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var c *Context
	var err error

	if c, err = NewContext(); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	defer c.Close()

	// Execute Middleware
	for _, m := range r.middleware {
		m(req, c)
	}

	// Execute the handler
	if r.handler != nil {
		err = r.handler(w, req, c)
	} else if r.interceptor != nil {
		rh := r.interceptor(w, req, c)
		err = rh(w, req, c)
	}

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func methodList(methods int) (r []string) {
	if methods&GET == GET {
		r = append(r, methodGet)
	}

	if methods&POST == POST {
		r = append(r, methodPost)
	}

	if methods&PUT == PUT {
		r = append(r, methodPut)
	}

	if methods&DELETE == DELETE {
		r = append(r, methodDelete)
	}

	return
}

func NewGoat() *Goat {
	// Initialize session store
	gob.Register(bson.ObjectId(""))
	s := sessions.NewCookieStore([]byte("sevenbyelevensecretbomberboy"))
	r := mux.NewRouter()

	mx := http.NewServeMux()
	mx.Handle("/", r)

	return &Goat{
		Router:       r,
		sessionstore: s,
		routes:       make(map[string]*route),
		servemux:     mx,
	}
}

func (g *Goat) RegisterRoute(path, name string, method int, handler interface{}) {
	// Initialize the HTTP router
	r := new(route)
	r.Goat = g
	r.path = path
	r.name = name

	if g.routes[r.name] != nil {
		return
	}

	g.routes[r.name] = r

	if h, ok := handler.(func(http.ResponseWriter, *http.Request, *Context) error); ok {
		r.handler = h
	} else if h, ok := handler.(Handler); ok {
		r.handler = h
	} else if i, ok := handler.(Interceptor); ok {
		r.interceptor = i
	} else if h, ok := handler.(http.Handler); ok {
		g.Router.Handle(path, h)
		return
	} else {
		panic("Unknown handler passed to RegisterRoute")
	}

	methods := methodList(method)
	g.Router.Handle(path, r).Methods(methods...).Name(r.name)
}

func (g *Goat) CopyDB() *mgo.Database {
	return g.dbsession.Copy().DB(g.dbname)
}

func (g *Goat) CloneDB() *mgo.Database {
	return g.dbsession.Clone().DB(g.dbname)
}

func (g *Goat) RegisterStaticFileHandler(remote, local string) {
	// Static file handler
	http.Handle(remote, http.FileServer(http.Dir(local)))
}

func (g *Goat) RegisterMiddleware(m Middleware) {
	g.middleware = append(g.middleware, m)
}

func (g *Goat) Reverse(root string, params ...string) (*url.URL, error) {
	return g.Router.Get(root).URL(params...)
}

func (g *Goat) ListenAndServe(port string) error {
	p := 8080

	if port != "" {
		p, _ = strconv.Atoi(port)
	}

	server := &http.Server{
		Handler: g.servemux,
	}

	g.listener, _ = net.ListenTCP("tcp", &net.TCPAddr{
		Port: p,
	})

	return server.Serve(g.listener)
}

func (g *Goat) ListenAndServeTLS(cert, key, addr string) error {
	return spdy.ListenAndServeTLS(addr, cert, key, g.servemux)
}

func (g *Goat) Close() {
	g.listener.Close()
}
<commit_msg>SPDY is a configurable option<commit_after>/****************************************************************************
 * Copyright (c) 2013, Scott Ferguson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the software nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

package goat

import (
	"encoding/gob"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/scottferg/gospdy"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"net"
	"net/http"
	"net/url"
	"strconv"
)

const (
	GET    = 1 << 0
	POST   = 1 << 1
	PUT    = 1 << 2
	DELETE = 1 << 3

	methodGet    = "GET"
	methodPost   = "POST"
	methodPut    = "PUT"
	methodDelete = "DELETE"
)

type Config struct {
	Spdy bool
}

type Goat struct {
	Router       *mux.Router
	Config       Config
	routes       map[string]*route
	middleware   []Middleware
	dbsession    *mgo.Session
	dbname       string
	sessionstore sessions.Store
	listener     *net.TCPListener
	servemux     *http.ServeMux
}

type Handler func(http.ResponseWriter, *http.Request, *Context) error

type route struct {
	*Goat
	path        string
	name        string
	handler     Handler
	interceptor Interceptor
}

func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var c *Context
	var err error

	if c, err = NewContext(); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	defer c.Close()

	// Execute Middleware
	for _, m := range r.middleware {
		m(req, c)
	}

	// Execute the handler
	if r.handler != nil {
		err = r.handler(w, req, c)
	} else if r.interceptor != nil {
		rh := r.interceptor(w, req, c)
		err = rh(w, req, c)
	}

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func methodList(methods int) (r []string) {
	if methods&GET == GET {
		r = append(r, methodGet)
	}

	if methods&POST == POST {
		r = append(r, methodPost)
	}

	if methods&PUT == PUT {
		r = append(r, methodPut)
	}

	if methods&DELETE == DELETE {
		r = append(r, methodDelete)
	}

	return
}

func New(c *Config) *Goat {
	// Initialize session store
	gob.Register(bson.ObjectId(""))
	s := sessions.NewCookieStore([]byte("sevenbyelevensecretbomberboy"))
	r := mux.NewRouter()

	mx := http.NewServeMux()
	mx.Handle("/", r)

	return &Goat{
		Router:       r,
		Config:       *c,
		sessionstore: s,
		routes:       make(map[string]*route),
		servemux:     mx,
	}
}

func (g *Goat) RegisterRoute(path, name string, method int, handler interface{}) {
	// Initialize the HTTP router
	r := new(route)
	r.Goat = g
	r.path = path
	r.name = name

	if g.routes[r.name] != nil {
		return
	}

	g.routes[r.name] = r

	if h, ok := handler.(func(http.ResponseWriter, *http.Request, *Context) error); ok {
		r.handler = h
	} else if h, ok := handler.(Handler); ok {
		r.handler = h
	} else if i, ok := handler.(Interceptor); ok {
		r.interceptor = i
	} else if h, ok := handler.(http.Handler); ok {
		g.Router.Handle(path, h)
		return
	} else {
		panic("Unknown handler passed to RegisterRoute")
	}

	methods := methodList(method)
	g.Router.Handle(path, r).Methods(methods...).Name(r.name)
}

func (g *Goat) CopyDB() *mgo.Database {
	return g.dbsession.Copy().DB(g.dbname)
}

func (g *Goat) CloneDB() *mgo.Database {
	return g.dbsession.Clone().DB(g.dbname)
}

func (g *Goat) RegisterStaticFileHandler(remote, local string) {
	// Static file handler
	http.Handle(remote, http.FileServer(http.Dir(local)))
}

func (g *Goat) RegisterMiddleware(m Middleware) {
	g.middleware = append(g.middleware, m)
}

func (g *Goat) Reverse(root string, params ...string) (*url.URL, error) {
	return g.Router.Get(root).URL(params...)
}

func (g *Goat) ListenAndServe(port string) error {
	p := 8080

	if port != "" {
		p, _ = strconv.Atoi(port)
	}

	server := &http.Server{
		Handler: g.servemux,
	}

	g.listener, _ = net.ListenTCP("tcp", &net.TCPAddr{
		Port: p,
	})

	return server.Serve(g.listener)
}

func (g *Goat) ListenAndServeTLS(cert, key, addr string) error {
	if g.Config.Spdy {
		return spdy.ListenAndServeTLS(addr, cert, key, g.servemux)
	}

	server := &http.Server{
		Addr:    addr,
		Handler: g.servemux,
	}
	return server.ListenAndServeTLS(cert, key)
}

func (g *Goat) Close() {
	g.listener.Close()
}
<|endoftext|>


<commit_before>package main

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

	"github.com/nsf/termbox-go"
)

const VERSION = "0.0.1"

// Editor modes
const (
	ModeEdit    = 0
	ModeInsert  = 1
	ModeCommand = 2
	ModeQuit    = 9999
)

// A row of text in the editor
type Row struct {
	Text string
}

func NewRow(text string) Row {
	r := Row{}
	r.Text = strings.Replace(text, "\t", "    ", -1)
	return r
}

func (r *Row) DisplayText() string {
	return r.Text
}

func (r *Row) Length() int {
	return len(r.DisplayText())
}

func (r *Row) InsertChar(position int, c rune) {
	line := ""
	if position <= len(r.Text) {
		line += r.Text[0:position]
	} else {
		line += r.Text
	}
	line += string(c)
	if position < len(r.Text) {
		line += r.Text[position+1:]
	}
	r.Text = line
}

func (r *Row) DeleteChar(position int) {
	if position > r.Length() {
		position = r.Length() - 1
	}
	if position < 0 {
		return
	}
	r.Text = r.Text[0:position] + r.Text[position+1:]
}

// The Editor
type Editor struct {
	Mode       int
	ScreenRows int
	ScreenCols int
	EditRows   int // actual number of rows used for text editing
	EditCols   int
	CursorRow  int
	CursorCol  int
	Message    string // status message
	Rows       []Row
	RowOffset  int
	ColOffset  int
	FileName   string
	Command    string
}

func NewEditor() *Editor {
	e := &Editor{}
	e.Rows = make([]Row, 0)
	e.Mode = ModeEdit
	return e
}

func (e *Editor) ReadFile(path string) error {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	s := string(b)
	lines := strings.Split(s, "\n")
	e.Rows = make([]Row, 0)
	for _, line := range lines {
		e.Rows = append(e.Rows, NewRow(line))
	}
	e.FileName = path
	return nil
}

func (e *Editor) WriteFile(path string) error {
	if false {
		b, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}
		s := string(b)
		lines := strings.Split(s, "\n")
		e.Rows = make([]Row, 0)
		for _, line := range lines {
			e.Rows = append(e.Rows, NewRow(line))
		}
		e.FileName = path
	}
	return nil
}

func (e *Editor) PerformCommand() {

	parts := strings.Split(e.Command, " ")
	if len(parts) > 0 {

		i, err := strconv.ParseInt(parts[0], 10, 64)
		if err == nil {
			e.CursorRow = int(i - 1)
			if e.CursorRow > len(e.Rows)-1 {
				e.CursorRow = len(e.Rows) - 1
			}
			if e.CursorRow < 0 {
				e.CursorRow = 0
			}
		}
		switch parts[0] {
		case "q":
			e.Mode = ModeQuit
			return
		case "r":
			if len(parts) == 2 {
				filename := parts[1]
				e.ReadFile(filename)
			}
		case "w":
			var filename string
			if len(parts) == 2 {
				filename = parts[2]
			} else {
				filename = e.FileName
			}
			e.WriteFile(filename)
		case "$":
			e.CursorRow = len(e.Rows) - 1
			if e.CursorRow < 0 {
				e.CursorRow = 0
			}
		default:
			e.Message = "hey hey hey"
		}
	}
	e.Command = ""
	e.Mode = ModeEdit
}

func (e *Editor) ProcessNextEvent() error {
	event := termbox.PollEvent()

	e.Message = fmt.Sprintf("event=%+v", event)

	switch event.Type {
	case termbox.EventResize:
		termbox.Flush()
		return nil
	case termbox.EventKey:
		break // handle these below
	default:
		return nil
	}

	switch e.Mode {

	case ModeEdit:
		key := event.Key
		if key != 0 {
			switch key {
			case termbox.KeyEsc:
				break
			case termbox.KeyPgup:
				e.CursorRow = e.RowOffset
				for i := 0; i < e.EditRows; i++ {
					e.MoveCursor(termbox.KeyArrowUp)
				}
			case termbox.KeyPgdn:
				e.CursorRow = e.RowOffset + e.EditRows - 1
				for i := 0; i < e.EditRows; i++ {
					e.MoveCursor(termbox.KeyArrowDown)
				}
			case termbox.KeyCtrlA, termbox.KeyHome:
				e.CursorCol = 0
			case termbox.KeyCtrlE, termbox.KeyEnd:
				e.CursorCol = 0
				if e.CursorRow < len(e.Rows) {
					e.CursorCol = e.Rows[e.CursorRow].Length() - 1
					if e.CursorCol < 0 {
						e.CursorCol = 0
					}
				}
			case termbox.KeyArrowUp, termbox.KeyArrowDown, termbox.KeyArrowLeft, termbox.KeyArrowRight:
				e.MoveCursor(key)
			}
		}
		ch := event.Ch
		if ch != 0 {
			switch ch {
			case ':':
				e.Mode = ModeCommand
				e.Command = ""
			case 'h':
				e.MoveCursor(termbox.KeyArrowLeft)
			case 'j':
				e.MoveCursor(termbox.KeyArrowDown)
			case 'k':
				e.MoveCursor(termbox.KeyArrowUp)
			case 'l':
				e.MoveCursor(termbox.KeyArrowRight)
			case 'i':
				e.Mode = ModeInsert
			case 'x':
				e.DeleteChar()
			}
		}

	case ModeInsert:
		key := event.Key
		if key != 0 {
			switch key {
			case termbox.KeyEsc:
				e.Mode = ModeEdit
			}
		}
		ch := event.Ch
		if ch != 0 {
			e.InsertChar(ch)
		}

	case ModeCommand:
		key := event.Key
		if key != 0 {
			switch key {
			case termbox.KeyEnter:
				e.PerformCommand()
			case termbox.KeyBackspace2:
				if len(e.Command) > 0 {
					e.Command = e.Command[0 : len(e.Command)-1]
				}
			case termbox.KeySpace:
				e.Command += " "
			}
		}
		ch := event.Ch
		if ch != 0 {
			e.Command = e.Command + string(ch)
		}
	}

	return nil
}

func (e *Editor) Scroll() {
	if e.CursorRow < e.RowOffset {
		e.RowOffset = e.CursorRow
	}
	if e.CursorRow-e.RowOffset >= e.EditRows {
		e.RowOffset = e.CursorRow - e.EditRows + 1
	}
	if e.CursorCol < e.ColOffset {
		e.ColOffset = e.CursorCol
	}
	if e.CursorCol-e.ColOffset >= e.EditCols {
		e.ColOffset = e.CursorCol - e.EditCols + 1
	}
}

func (e *Editor) DrawScreen() {
	w, h := termbox.Size()
	e.ScreenRows = h
	e.ScreenCols = w
	e.EditRows = e.ScreenRows - 2
	e.EditCols = e.ScreenCols

	e.Scroll()
	buffer := make([]byte, 0)
	buffer = append(buffer, []byte("\x1b[?25l")...) // hide cursor
	buffer = append(buffer, []byte("\x1b[1;1H")...) // move cursor to row 1, col 1
	buffer = e.DrawRows(buffer)

	buffer = append(buffer, []byte(fmt.Sprintf("\x1b[%d;%dH", e.CursorRow+1-e.RowOffset, e.CursorCol+1-e.ColOffset))...)

	termbox.SetCursor(e.CursorRow+1-e.RowOffset, e.CursorCol+1-e.ColOffset)

	buffer = append(buffer, []byte("\x1b[?25h")...) // show cursor
	os.Stdout.Write(buffer)
}

func (e *Editor) DrawRows(buffer []byte) []byte {
	for y := 0; y < e.ScreenRows; y++ {
		if y == e.ScreenRows-2 {
			// draw status bar
			buffer = append(buffer, []byte("\x1b[7m")...)
			finalText := fmt.Sprintf(" %d/%d ", e.CursorRow, len(e.Rows))
			text := " " + e.FileName + " "
			for len(text) < e.ScreenCols-len(finalText)-1 {
				text = text + " "
			}
			text += finalText
			buffer = append(buffer, []byte(text)...)
			buffer = append(buffer, []byte("\x1b[K")...)
			buffer = append(buffer, []byte("\x1b[m")...)
			buffer = append(buffer, []byte("\r\n")...)
		} else if y == e.ScreenRows-1 {

			if e.Mode == ModeCommand {
				buffer = append(buffer, []byte(":"+e.Command)...)
				buffer = append(buffer, []byte("\x1b[K")...)
			} else {
				// draw message bar
				text := e.Message
				if len(text) > e.ScreenCols {
					text = text[0:e.ScreenCols]
				}
				buffer = append(buffer, []byte(text)...)
				buffer = append(buffer, []byte("\x1b[K")...)
			}
		} else if (y + e.RowOffset) < len(e.Rows) {
			// draw editor text
			line := e.Rows[y+e.RowOffset].DisplayText()
			if e.ColOffset < len(line) {
				line = line[e.ColOffset:]
			} else {
				line = ""
			}
			if len(line) > e.ScreenCols {
				line = line[0:e.ScreenCols]
			}
			buffer = append(buffer, []byte(line)...)
			buffer = append(buffer, []byte("\x1b[K")...)
			buffer = append(buffer, []byte("\r\n")...)
		} else {
			if y == e.ScreenRows/3 {
				welcome := fmt.Sprintf("goed editor -- version %s", VERSION)
				padding := (e.ScreenCols - len(welcome)) / 2
				buffer = append(buffer, []byte("~")...)
				for i := 1; i <= padding; i++ {
					buffer = append(buffer, []byte(" ")...)
				}
				buffer = append(buffer, []byte(welcome)...)
			} else {
				buffer = append(buffer, []byte("~")...)
			}
			buffer = append(buffer, []byte("\x1b[K")...)
			buffer = append(buffer, []byte("\r\n")...)
		}
	}
	return buffer
}

func (e *Editor) MoveCursor(key termbox.Key) {
	switch key {
	case termbox.KeyArrowLeft:
		if e.CursorCol > 0 {
			e.CursorCol--
		} else if e.CursorRow > 0 {
			// wrap around
			e.CursorRow--
			e.CursorCol = e.Rows[e.CursorRow].Length() - 1
		}
	case termbox.KeyArrowRight:
		if e.CursorRow < len(e.Rows) {
			rowLength := e.Rows[e.CursorRow].Length()
			if e.CursorCol < rowLength-1 {
				e.CursorCol++
			} else if e.CursorRow < len(e.Rows)-1 {
				// wrap around
				e.CursorRow++
				e.CursorCol = 0
			}
		}
	case termbox.KeyArrowUp:
		if e.CursorRow > 0 {
			e.CursorRow--
		}
	case termbox.KeyArrowDown:
		if e.CursorRow < len(e.Rows)-1 {
			e.CursorRow++
		}
	}
	// don't go past the end of the current line
	if e.CursorRow < len(e.Rows) {
		rowLength := e.Rows[e.CursorRow].Length()
		if e.CursorCol > rowLength-1 {
			e.CursorCol = rowLength - 1
			if e.CursorCol < 0 {
				e.CursorCol = 0
			}
		}
	}
}

func (e *Editor) InsertChar(c rune) {
	if len(e.Rows) == 0 {
		e.Rows = append(e.Rows, NewRow(""))
	}
	e.Rows[e.CursorRow].InsertChar(e.CursorCol, c)
	e.CursorCol += 1
}

func (e *Editor) DeleteChar() {
	if len(e.Rows) > 0 {
		e.Rows[e.CursorRow].DeleteChar(e.CursorCol)
		if e.CursorCol > e.Rows[e.CursorRow].Length()-1 {
			e.CursorCol--
		}
	}
}

func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	e := NewEditor()
	for e.Mode != ModeQuit {
		e.DrawScreen()
		e.ProcessNextEvent()
	}
}
<commit_msg>dd deletion and carriage return insertion in the middle of lines<commit_after>package main

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

	"github.com/nsf/termbox-go"
)

const VERSION = "0.0.1"

// Editor modes
const (
	ModeEdit    = 0
	ModeInsert  = 1
	ModeCommand = 2
	ModeQuit    = 9999
)

// A row of text in the editor
type Row struct {
	Text string
}

func NewRow(text string) Row {
	r := Row{}
	r.Text = strings.Replace(text, "\t", "    ", -1)
	return r
}

func (r *Row) DisplayText() string {
	return r.Text
}

func (r *Row) Length() int {
	return len(r.DisplayText())
}

func (r *Row) InsertChar(position int, c rune) {
	line := ""
	if position <= len(r.Text) {
		line += r.Text[0:position]
	} else {
		line += r.Text
	}
	line += string(c)
	if position < len(r.Text) {
		line += r.Text[position:]
	}
	r.Text = line
}

// delete
func (r *Row) DeleteChar(position int) {
	if r.Length() == 0 {
		return
	}
	if position > r.Length()-1 {
		position = r.Length() - 1
	}
	r.Text = r.Text[0:position] + r.Text[position+1:]
}

// split
func (r *Row) Split(position int) Row {
	before := r.Text[0:position]
	after := r.Text[position:]
	r.Text = before
	return NewRow(after)
}

// The Editor
type Editor struct {
	Mode        int
	ScreenRows  int
	ScreenCols  int
	EditRows    int // actual number of rows used for text editing
	EditCols    int
	CursorRow   int
	CursorCol   int
	Message     string // status message
	Rows        []Row
	RowOffset   int
	ColOffset   int
	FileName    string
	Command     string
	CommandKeys string
}

func NewEditor() *Editor {
	e := &Editor{}
	e.Rows = make([]Row, 0)
	e.Mode = ModeEdit
	return e
}

func (e *Editor) ReadFile(path string) error {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	s := string(b)
	lines := strings.Split(s, "\n")
	e.Rows = make([]Row, 0)
	for _, line := range lines {
		e.Rows = append(e.Rows, NewRow(line))
	}
	e.FileName = path
	return nil
}

func (e *Editor) WriteFile(path string) error {
	if false {
		b, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}
		s := string(b)
		lines := strings.Split(s, "\n")
		e.Rows = make([]Row, 0)
		for _, line := range lines {
			e.Rows = append(e.Rows, NewRow(line))
		}
		e.FileName = path
	}
	return nil
}

func (e *Editor) PerformCommand() {

	parts := strings.Split(e.Command, " ")
	if len(parts) > 0 {

		i, err := strconv.ParseInt(parts[0], 10, 64)
		if err == nil {
			e.CursorRow = int(i - 1)
			if e.CursorRow > len(e.Rows)-1 {
				e.CursorRow = len(e.Rows) - 1
			}
			if e.CursorRow < 0 {
				e.CursorRow = 0
			}
		}
		switch parts[0] {
		case "q":
			e.Mode = ModeQuit
			return
		case "r":
			if len(parts) == 2 {
				filename := parts[1]
				e.ReadFile(filename)
			}
		case "w":
			var filename string
			if len(parts) == 2 {
				filename = parts[2]
			} else {
				filename = e.FileName
			}
			e.WriteFile(filename)
		case "$":
			e.CursorRow = len(e.Rows) - 1
			if e.CursorRow < 0 {
				e.CursorRow = 0
			}
		default:
			e.Message = "hey hey hey"
		}
	}
	e.Command = ""
	e.Mode = ModeEdit
}

func (e *Editor) ProcessNextEvent() error {
	event := termbox.PollEvent()

	//e.Message = fmt.Sprintf("event=%+v", event)

	switch event.Type {
	case termbox.EventResize:
		termbox.Flush()
		return nil
	case termbox.EventKey:
		break // handle these below
	default:
		return nil
	}

	switch e.Mode {

	case ModeEdit:

		if e.CommandKeys == "d" {

			ch := event.Ch
			if ch != 0 {
				switch ch {
				case 'd':
					e.DeleteRow()
				}
			}
			e.CommandKeys = ""
			return nil
		}

		key := event.Key
		if key != 0 {
			switch key {
			case termbox.KeyEsc:
				break
			case termbox.KeyPgup:
				e.CursorRow = e.RowOffset
				for i := 0; i < e.EditRows; i++ {
					e.MoveCursor(termbox.KeyArrowUp)
				}
			case termbox.KeyPgdn:
				e.CursorRow = e.RowOffset + e.EditRows - 1
				for i := 0; i < e.EditRows; i++ {
					e.MoveCursor(termbox.KeyArrowDown)
				}
			case termbox.KeyCtrlA, termbox.KeyHome:
				e.CursorCol = 0
			case termbox.KeyCtrlE, termbox.KeyEnd:
				e.CursorCol = 0
				if e.CursorRow < len(e.Rows) {
					e.CursorCol = e.Rows[e.CursorRow].Length() - 1
					if e.CursorCol < 0 {
						e.CursorCol = 0
					}
				}
			case termbox.KeyArrowUp, termbox.KeyArrowDown, termbox.KeyArrowLeft, termbox.KeyArrowRight:
				e.MoveCursor(key)
			}
		}
		ch := event.Ch
		if ch != 0 {
			switch ch {
			case ':':
				e.Mode = ModeCommand
				e.Command = ""
			case 'h':
				e.MoveCursor(termbox.KeyArrowLeft)
			case 'j':
				e.MoveCursor(termbox.KeyArrowDown)
			case 'k':
				e.MoveCursor(termbox.KeyArrowUp)
			case 'l':
				e.MoveCursor(termbox.KeyArrowRight)
			case 'i':
				e.Mode = ModeInsert
			case 'x':
				e.DeleteChar()
			case 'd':
				e.CommandKeys = "d"
			}
		}

	case ModeInsert:
		key := event.Key
		if key != 0 {
			switch key {
			case termbox.KeyEsc:
				e.Mode = ModeEdit
			case termbox.KeyEnter:
				e.InsertRow()
				e.CursorRow++
				e.CursorCol = 0
			case termbox.KeySpace:
				e.InsertChar(' ')
			}
		}
		ch := event.Ch
		if ch != 0 {
			e.InsertChar(ch)
		}

	case ModeCommand:
		key := event.Key
		if key != 0 {
			switch key {
			case termbox.KeyEnter:
				e.PerformCommand()
			case termbox.KeyBackspace2:
				if len(e.Command) > 0 {
					e.Command = e.Command[0 : len(e.Command)-1]
				}
			case termbox.KeySpace:
				e.Command += " "
			}
		}
		ch := event.Ch
		if ch != 0 {
			e.Command = e.Command + string(ch)
		}
	}

	return nil
}

func (e *Editor) Scroll() {
	if e.CursorRow < e.RowOffset {
		e.RowOffset = e.CursorRow
	}
	if e.CursorRow-e.RowOffset >= e.EditRows {
		e.RowOffset = e.CursorRow - e.EditRows + 1
	}
	if e.CursorCol < e.ColOffset {
		e.ColOffset = e.CursorCol
	}
	if e.CursorCol-e.ColOffset >= e.EditCols {
		e.ColOffset = e.CursorCol - e.EditCols + 1
	}
}

func (e *Editor) DrawScreen() {
	w, h := termbox.Size()
	e.ScreenRows = h
	e.ScreenCols = w
	e.EditRows = e.ScreenRows - 2
	e.EditCols = e.ScreenCols

	e.Scroll()
	buffer := make([]byte, 0)
	buffer = append(buffer, []byte("\x1b[?25l")...) // hide cursor
	buffer = append(buffer, []byte("\x1b[1;1H")...) // move cursor to row 1, col 1
	buffer = e.DrawRows(buffer)

	buffer = append(buffer, []byte(fmt.Sprintf("\x1b[%d;%dH", e.CursorRow+1-e.RowOffset, e.CursorCol+1-e.ColOffset))...)

	termbox.SetCursor(e.CursorRow+1-e.RowOffset, e.CursorCol+1-e.ColOffset)

	buffer = append(buffer, []byte("\x1b[?25h")...) // show cursor
	os.Stdout.Write(buffer)
}

func (e *Editor) DrawRows(buffer []byte) []byte {
	for y := 0; y < e.ScreenRows; y++ {
		if y == e.ScreenRows-2 {
			// draw status bar
			buffer = append(buffer, []byte("\x1b[7m")...)
			finalText := fmt.Sprintf(" %d/%d ", e.CursorRow, len(e.Rows))
			text := " " + e.FileName + " "
			for len(text) < e.ScreenCols-len(finalText)-1 {
				text = text + " "
			}
			text += finalText
			buffer = append(buffer, []byte(text)...)
			buffer = append(buffer, []byte("\x1b[K")...)
			buffer = append(buffer, []byte("\x1b[m")...)
			buffer = append(buffer, []byte("\r\n")...)
		} else if y == e.ScreenRows-1 {

			if e.Mode == ModeCommand {
				buffer = append(buffer, []byte(":"+e.Command)...)
				buffer = append(buffer, []byte("\x1b[K")...)
			} else {
				// draw message bar
				text := e.Message
				if len(text) > e.ScreenCols {
					text = text[0:e.ScreenCols]
				}
				buffer = append(buffer, []byte(text)...)
				buffer = append(buffer, []byte("\x1b[K")...)
			}
		} else if (y + e.RowOffset) < len(e.Rows) {
			// draw editor text
			line := e.Rows[y+e.RowOffset].DisplayText()
			if e.ColOffset < len(line) {
				line = line[e.ColOffset:]
			} else {
				line = ""
			}
			if len(line) > e.ScreenCols {
				line = line[0:e.ScreenCols]
			}
			buffer = append(buffer, []byte(line)...)
			buffer = append(buffer, []byte("\x1b[K")...)
			buffer = append(buffer, []byte("\r\n")...)
		} else {
			if y == e.ScreenRows/3 {
				welcome := fmt.Sprintf("goed editor -- version %s", VERSION)
				padding := (e.ScreenCols - len(welcome)) / 2
				buffer = append(buffer, []byte("~")...)
				for i := 1; i <= padding; i++ {
					buffer = append(buffer, []byte(" ")...)
				}
				buffer = append(buffer, []byte(welcome)...)
			} else {
				buffer = append(buffer, []byte("~")...)
			}
			buffer = append(buffer, []byte("\x1b[K")...)
			buffer = append(buffer, []byte("\r\n")...)
		}
	}
	return buffer
}

func (e *Editor) MoveCursor(key termbox.Key) {
	switch key {
	case termbox.KeyArrowLeft:
		if e.CursorCol > 0 {
			e.CursorCol--
		}
	case termbox.KeyArrowRight:
		if e.CursorRow < len(e.Rows) {
			rowLength := e.Rows[e.CursorRow].Length()
			if e.CursorCol < rowLength-1 {
				e.CursorCol++
			}
		}
	case termbox.KeyArrowUp:
		if e.CursorRow > 0 {
			e.CursorRow--
		}
	case termbox.KeyArrowDown:
		if e.CursorRow < len(e.Rows)-1 {
			e.CursorRow++
		}
	}
	// don't go past the end of the current line
	if e.CursorRow < len(e.Rows) {
		rowLength := e.Rows[e.CursorRow].Length()
		if e.CursorCol > rowLength-1 {
			e.CursorCol = rowLength - 1
			if e.CursorCol < 0 {
				e.CursorCol = 0
			}
		}
	}
}

func (e *Editor) InsertChar(c rune) {
	if len(e.Rows) == 0 {
		e.Rows = append(e.Rows, NewRow(""))
	}
	e.Rows[e.CursorRow].InsertChar(e.CursorCol, c)
	e.CursorCol += 1
}

func (e *Editor) DeleteChar() {
	if len(e.Rows) > 0 {
		e.Rows[e.CursorRow].DeleteChar(e.CursorCol)
		if e.CursorCol > e.Rows[e.CursorRow].Length()-1 {
			e.CursorCol--
		}
		if e.CursorCol < 0 {
			e.CursorCol = 0
		}
	}
}

func (e *Editor) InsertRow() {
	if e.CursorRow > len(e.Rows)-1 {
		e.Rows = append(e.Rows, NewRow(""))
		e.CursorRow = len(e.Rows) - 1
	} else {
		position := e.CursorRow
		newRow := e.Rows[position].Split(e.CursorCol)
		e.Message = newRow.Text
		i := position + 1
		e.Rows = append(e.Rows, NewRow(""))
		copy(e.Rows[i+1:], e.Rows[i:])
		e.Rows[i] = newRow
	}
}

func (e *Editor) DeleteRow() {
	if len(e.Rows) == 0 {
		return
	}
	position := e.CursorRow
	e.Rows = append(e.Rows[0:position], e.Rows[position+1:]...)
	if position > len(e.Rows)-1 {
		position = len(e.Rows) - 1
	}
	if position < 0 {
		position = 0
	}
	e.CursorRow = position
}

func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	e := NewEditor()
	for e.Mode != ModeQuit {
		e.DrawScreen()
		e.ProcessNextEvent()
	}
}
<|endoftext|>


<commit_before>// gogo is an alternative build tool for the Go programming language.
//
// Package gogo provides the basic types and interfaces to describe Projects and
// Packages.
//
// The gogo command can be installed with go get
//
//	go get github.com/davecheney/gogo
package gogo

// A Future represents some work to be performed.
type Future interface {
	// Result returns the result of the work as an error, or nil if the work
	// was performed successfully.
	// Implementers must observe these invariants
	// 1. There may be multiple concurrent callers to Result, or Result may
	//     be called many times in sequence, it must always return the same
	//     value.
	// 2. Result blocks until the work has been performed.
	Result() error
}
<commit_msg>fix go get<commit_after>// gogo is an alternative build tool for the Go programming language.
//
// Package gogo provides the basic types and interfaces to describe Projects and
// Packages.
//
// The gogo command can be installed with go get
//
//	go get github.com/davecheney/gogo/gogo
//
package gogo

// A Future represents some work to be performed.
type Future interface {
	// Result returns the result of the work as an error, or nil if the work
	// was performed successfully.
	// Implementers must observe these invariants
	// 1. There may be multiple concurrent callers to Result, or Result may
	//     be called many times in sequence, it must always return the same
	//     value.
	// 2. Result blocks until the work has been performed.
	Result() error
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"strconv"
	"time"
	"github.com/knieriem/markdown"
)

type Article struct {
	name string
	date time.Time
	path string
	html string
}

type ByDate []Article

func (a ByDate) Len() int { return len(a ) }
func (a ByDate) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByDate) Less(i, j int) bool { return a[i].date.After(a[j].date) }

var (
	articles []Article
)

func fetchArticles() {
	walkErr := filepath.Walk("posts/", visit)
	if walkErr != nil {
		fmt.Printf("Walk error: %s", walkErr)
	}
	sort.Sort(ByDate(articles))
}

func visit(path string, info os.FileInfo, err error) error {
	splitPath := strings.Split(path, "/")
	if len(splitPath) > 4 {
		name := splitPath[len(splitPath)-1]
		name = name[:len(name)-len(".md")]
		year, err := strconv.Atoi(splitPath[1])
		month, err := strconv.Atoi(splitPath[2])
		day, err := strconv.Atoi(splitPath[3])
		if err != nil {
			return nil //TODO: Error handling
		}
		date := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)

		var buf bytes.Buffer
		file, err := os.Open(path)
		if err != nil {
			fmt.Printf("Open error: %s", err)
			return nil //TODO: Error handling
		}
		defer file.Close()
		p := markdown.NewParser(nil)
		p.Markdown(file, markdown.ToHTML(&buf))

		article := Article{name, date, path, buf.String()}
		articles = append(articles, article)
		fmt.Printf("\nPath: %s\nDate: %s\nName: %s\nHTML:%s\n\n", path, date.String(), name, buf)
	}
	return nil
}

func renderPage(page bytes.Buffer, w http.ResponseWriter) {
	header, _ := ioutil.ReadFile("templates/header.html")
	footer, _ := ioutil.ReadFile("templates/footer.html")
	fmt.Fprintf(w, "%s %s %s", header, page.String(), footer)
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var page bytes.Buffer
	if len(articles) == 0 {
		fetchArticles()
	}
	for _, article := range articles {
		fmt.Printf("Article: %s", article.name)
		page.Write([]byte(article.html))
	}
	renderPage(page, w)
}

func clearCache() {
	articles = nil
}

func main() {
	ticker := time.NewTicker(time.Minute * 30)
	go func() {
		for _ = range ticker.C {
			clearCache()
		}
	}()
	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("./assets"))))
	http.HandleFunc("/", indexHandler)
	http.ListenAndServe(":8181", nil)
}

<commit_msg>Clean up stray newline<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"strconv"
	"time"
	"github.com/knieriem/markdown"
)

type Article struct {
	name string
	date time.Time
	path string
	html string
}

type ByDate []Article

func (a ByDate) Len() int { return len(a ) }
func (a ByDate) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByDate) Less(i, j int) bool { return a[i].date.After(a[j].date) }

var (
	articles []Article
)

func fetchArticles() {
	walkErr := filepath.Walk("posts/", visit)
	if walkErr != nil {
		fmt.Printf("Walk error: %s", walkErr)
	}
	sort.Sort(ByDate(articles))
}

func visit(path string, info os.FileInfo, err error) error {
	splitPath := strings.Split(path, "/")
	if len(splitPath) > 4 {
		name := splitPath[len(splitPath)-1]
		name = name[:len(name)-len(".md")]
		year, err := strconv.Atoi(splitPath[1])
		month, err := strconv.Atoi(splitPath[2])
		day, err := strconv.Atoi(splitPath[3])
		if err != nil {
			return nil //TODO: Error handling
		}
		date := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)

		var buf bytes.Buffer
		file, err := os.Open(path)
		if err != nil {
			fmt.Printf("Open error: %s", err)
			return nil //TODO: Error handling
		}
		defer file.Close()
		p := markdown.NewParser(nil)
		p.Markdown(file, markdown.ToHTML(&buf))

		article := Article{name, date, path, buf.String()}
		articles = append(articles, article)
		fmt.Printf("\nPath: %s\nDate: %s\nName: %s\nHTML:%s\n\n", path, date.String(), name, buf)
	}
	return nil
}

func renderPage(page bytes.Buffer, w http.ResponseWriter) {
	header, _ := ioutil.ReadFile("templates/header.html")
	footer, _ := ioutil.ReadFile("templates/footer.html")
	fmt.Fprintf(w, "%s %s %s", header, page.String(), footer)
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var page bytes.Buffer
	if len(articles) == 0 {
		fetchArticles()
	}
	for _, article := range articles {
		fmt.Printf("Article: %s", article.name)
		page.Write([]byte(article.html))
	}
	renderPage(page, w)
}

func clearCache() {
	articles = nil
}

func main() {
	ticker := time.NewTicker(time.Minute * 30)
	go func() {
		for _ = range ticker.C {
			clearCache()
		}
	}()
	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("./assets"))))
	http.HandleFunc("/", indexHandler)
	http.ListenAndServe(":8181", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"time"
)

var (
	hashLimit                 int    = 30
	test_command              string = "./gshs_test.bash"
	suffix                    string = "" // If not empty default, fix the flag default below.
	logPrefix                 string = "GSHS_LAST_"
	verbose                   bool   = false
	timeout                   int    = 30 // Timeout to apply to command; failure if hit
	hash_ev_name                     = "GOSSAHASH"
	function_selection_string        = hash_ev_name + " triggered"
	fail                      bool
)

const (
	FAILED = iota
	PASSED
	PASSED0 // no hits, thus passed.
	DONE
	DONE0 // not supposed to happen; failure without adding anything new.
)

// saveLogFiles stores data in filename, unless it cannot
// in which case it whines (but still returns).
// The default permission on the file name is conservative
// because "you never know".
func saveLogFile(filename string, data []byte) {
	error := ioutil.WriteFile(filename, data, 0600)
	if error != nil {
		fmt.Fprintf(os.Stderr, "Error saving log file %s\n", error)
	}
}

type arg []string

var args arg = arg{test_command}

func (a *arg) String() string {
	return fmt.Sprintf("%v", *a)
}

func (a *arg) Set(value string) error {
	*a = append([]string(*a), value)
	return nil
}

var hashes []string

// hashes below this correspond to a single function whose
// compilation is necessary to trigger a failure.
var next_singleton_hash_index int

// hashes below this are confirmed to contain a function
// whose compilation is necessary to trigger a failure.
// failure is either directly verified or inferred.
// var next_confirmed_hash_index int

// tryCmd runs the test command with suffix appended to the args.
// If timeout is greater than zero then the command will be
// killed after that many seconds (to help with bugs that exhibit
// as an infinite loop), otherwise it runs to completion and the
// error code and output are captureed and returned.
func tryCmd(suffix string) (output []byte, err error) {
	cmd := exec.Command(test_command)
	cmd.Args = append(cmd.Args, args...)
	cmd.Env = os.Environ()
	extraenv := make([]string, 0)

	ev := fmt.Sprintf("%s=%s", hash_ev_name, suffix)
	cmd.Env = append(cmd.Env, ev)
	extraenv = append(extraenv, ev)

	for i := 0; i < len(hashes); i++ {
		ev = fmt.Sprintf("%s%d=%s", hash_ev_name, i, hashes[i])
		cmd.Env = append(cmd.Env, ev)
		extraenv = append(extraenv, ev)
	}

	if verbose {
		fmt.Fprintf(os.Stdout, "Trying %s, %v + %s\n", test_command, cmd.Args, extraenv)
	} else {
		if len(extraenv) == 0 {
			fmt.Fprintf(os.Stdout, "Trying %s\n", suffix)
		} else {
			fmt.Fprintf(os.Stdout, "Trying %s + %s\n", suffix, extraenv)
		}
	}

	if timeout <= 0 {
		output, err = cmd.CombinedOutput()
	} else {
		var b bytes.Buffer
		cmd.Stdout = &b
		cmd.Stderr = &b
		err = cmd.Start()
		if err != nil {
			return
		}
		var killErr error
		timer := time.AfterFunc(time.Second*time.Duration(timeout), func() {
			killErr = cmd.Process.Signal(os.Kill)
		})
		err = cmd.Wait()
		if killErr != nil {
			// Not sure what I would do with this,
			// and it could appear merely as the result of a lost race.
		}
		timer.Stop()
		output = b.Bytes()
	}

	if verbose {
		fmt.Fprintf(os.Stdout, "%s", string(output))
	}
	return
}

// trySuffix runs the test command passing it suffix as an argument,
// and returns PASSED/FAILED/DONE/DONE0 based on return code and occurrences
// of the function_selection_string within the output; if there is only
// one and the command fails, then the search is done.
// Appropriate log files and narrative are also produced.
func trySuffix(suffix string) int {
	function_selection_bytes := []byte(function_selection_string)
	output, error := tryCmd(suffix)
	count := bytes.Count(output, function_selection_bytes)

	if error != nil {
		// we like errors.
		fmt.Fprintf(os.Stdout, "%s failed: %s\n", test_command, error.Error())
		saveLogFile(logPrefix+"FAIL.log", output)
		if count <= 1 {
			fmt.Fprintf(os.Stdout, "Review %s for failing run\n", logPrefix+"FAIL.log")
			if count == 0 {
				return DONE0
			}
			return DONE
		}
		return FAILED
	}
	saveLogFile(logPrefix+"PASS.log", output)
	if count == 0 {
		return PASSED0
	}
	return PASSED
}

func main() {
	flag.Var(&args, "c", "executable file of one arg hashstring to run.\n"+
		"\tMay be repeated to supply leading args to command.\n\t") // default on next line

	flag.StringVar(&logPrefix, "l", logPrefix, "prefix of log file names ending ...{PASS,FAIL}.log")
	flag.IntVar(&hashLimit, "n", hashLimit, "maximum hash string length to try before giving up")
	flag.StringVar(&suffix, "P", suffix, "root string to begin searching at (default empty)")
	flag.BoolVar(&verbose, "v", verbose, "also print output of test script (default false)")
	flag.IntVar(&timeout, "t", timeout, "timeout in seconds for running test script, 0=run till done")
	flag.BoolVar(&fail, "f", fail, "act as a test program")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr,
			`
%s runs
`,
			os.Args[0])
	}

	flag.Parse()

	if fail {
		test()
		return
	}

	// Extract test command and args if supplied.
	if len(args) > 0 {
		test_command = args[0]
		args = args[1:]
	}

	// confirmed_suffix is a suffix that is confirmed (initially asserted)
	// to contain a failure.
	confirmed_suffix := suffix
loop:
	for len(confirmed_suffix) < hashLimit {
		suffix = "0" + confirmed_suffix
		first_result := trySuffix(suffix)
		switch first_result {
		case FAILED:
			// Suffix is confirmed to contain a failure,
			// but there is more than one match (function compiled)
			confirmed_suffix = suffix
			continue

		case PASSED0:
		case PASSED:
			// Suffix does not trigger a failure

		case DONE0:
			// Treat this like a "pass" -- this hashcode is not useful for failure.

		case DONE:
			// suffix caused exactly one function to be optimized
			// and the test also failed.
			if next_singleton_hash_index == len(hashes) {
				break loop
			}
			confirmed_suffix = hashes[next_singleton_hash_index]
			hashes[next_singleton_hash_index] = suffix
			next_singleton_hash_index++
			continue
		}
		suffix = "1" + confirmed_suffix
		switch trySuffix(suffix) {
		case FAILED:
			confirmed_suffix = suffix
			continue
		case PASSED:
			if first_result == PASSED {
				fmt.Fprintf(os.Stdout, "Both trials unexpectedly succeeded\n")
				// 0xyz and 1xyz both succeeded alone, but xyz failed.
				// Failure therefore requires at least 2 hits, one in
				// 0xyz and one in 1xyz.  Therefore, put 1xyz in the set
				// of confirmed (i.e., contains a non-isolated failure)
				// mark 0xyz as confirmed for local search, and continue.
				hashes = append(hashes, suffix)
				confirmed_suffix = "0" + confirmed_suffix
				continue
			}
			fallthrough

		case PASSED0, DONE0:
			fmt.Fprintf(os.Stdout, "Combination of empty and pass, discard path\n")
			if next_singleton_hash_index == len(hashes) {
				break loop
			}
			confirmed_suffix = hashes[len(hashes)-1]
			hashes = hashes[0 : len(hashes)-1]
			continue

		case DONE:
			if next_singleton_hash_index == len(hashes) {
				break loop
			}
			confirmed_suffix = hashes[next_singleton_hash_index]
			hashes[next_singleton_hash_index] = suffix
			next_singleton_hash_index++
			continue
		}
	}

	fmt.Fprintf(os.Stdout, "Finished with GOSSAHASH=%s\n", string(suffix))
	if len(hashes) > 0 {
		fmt.Printf("Before filtering, multiple methods required for failure:\nGOSSAHASH=%s", suffix)
		for i := 0; i < len(hashes); i++ {
			fmt.Printf(" %s%d=%s", hash_ev_name, i, hashes[i])
		}
		fmt.Println()
		// Next filter the hashes to see if any can be excluded:
		next_confirmed_hash_index := len(hashes) - 1
		temporarily_removed := hashes[next_confirmed_hash_index]
		hashes = hashes[0:next_confirmed_hash_index]
		for i := next_confirmed_hash_index; i >= -1 && next_confirmed_hash_index >= 0; i-- {
			// do a run excluding hashes[i], where hashes[-1] == suffix
			t := temporarily_removed
			if i == -1 {
				temporarily_removed = suffix
				suffix = t
			} else if i < len(hashes) {
				temporarily_removed = hashes[i]
				hashes[i] = t
			} // temporarily_removed is the excluded one if i == len(hashes)
			switch trySuffix(suffix) {
			case DONE0: // failed but GOSSAHASH triggered nothing
				// needed neither GOSSAHASH nor the excluded one.
				next_confirmed_hash_index--
				suffix = hashes[next_confirmed_hash_index]
				next_confirmed_hash_index--
				hashes = hashes[0:next_confirmed_hash_index]

			case DONE, FAILED: // ought not see failed....
				next_confirmed_hash_index--
				hashes = hashes[0:next_confirmed_hash_index]
			}
		}
		hashes = append(hashes, temporarily_removed)
		fmt.Printf("After filtering, methods required for failure:\nGOSSAHASH=%s", suffix)
		for i := 0; i < len(hashes); i++ {
			fmt.Printf(" %s%d=%s", hash_ev_name, i, hashes[i])
		}
		fmt.Println()

	}
}
<commit_msg>fixed filter, added randomization, added comments<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"os/exec"
	"time"
)

var (
	hashLimit    int    = 30 // Maximum length of a hash string
	test_command string = "./gshs_test.bash"
	suffix       string = ""           // The initial hash suffix assumed to cause failure.
	logPrefix    string = "GSHS_LAST_" // Prefix on PASS/FAIL log files.
	verbose      bool   = false
	timeout      int    = 30 // Timeout to apply to command; failure if hit

	// Name of the environment variable that contains the hash suffix to be matched against function name hashes.
	hash_ev_name = "GOSSAHASH"
	// Expect to see this in the output when a value for GOSSAHASH triggers SSA-compilation of a function.
	function_selection_string = hash_ev_name + " triggered"

	fail bool // If true, converts behavior to a test program
)

const (
	FAILED  = iota // Script exited with return code > 0 and multiple functions SSA compiled.
	DONE           // Script exited with return code > 0 and exactly one function SSA compiled.
	DONE0          // Script exited with return code > 0 and no functions SSA compiled (means test is flaky)
	PASSED         // Script exited with return code 0
	PASSED0        // Script exited with return code 0 AND no functions SSA compiled.
)

// saveLogFiles stores data in filename, unless it cannot
// in which case it whines (but still returns).
// The default permission on the file name is conservative
// because "you never know".
func saveLogFile(filename string, data []byte) {
	error := ioutil.WriteFile(filename, data, 0600)
	if error != nil {
		fmt.Fprintf(os.Stderr, "Error saving log file %s\n", error)
	}
}

type arg []string

var args arg = arg{test_command}

func (a *arg) String() string {
	return fmt.Sprintf("%v", *a)
}

func (a *arg) Set(value string) error {
	*a = append([]string(*a), value)
	return nil
}

// The accumulated list of hashes that are either proven
// singleton triggers that contribute to failure, or proven/
// inferred to trigger at least one SSA-compilation that
// contributes to failure.
var hashes []string

// hashes before  this index correspond to a single function
// whose compilation is necessary to trigger a failure.
// This counter advances as new singleton-triggering hashes
// are found.
var next_singleton_hash_index int

// tryCmd runs the test command with suffix and all the hashes
// assigned to environment variables of the form GOSSAHASH and
// GOSSAHASH%d for [0:len(hashes)-1]
// If timeout is greater than zero then the command will be
// killed after that many seconds (to help with bugs that exhibit
// as an infinite loop), otherwise it runs to completion and the
// error code and output are captured and returned.
func tryCmd(suffix string) (output []byte, err error) {
	cmd := exec.Command(test_command)
	cmd.Args = append(cmd.Args, args...)

	// Fill the env
	cmd.Env = os.Environ()
	extraenv := make([]string, 0)
	ev := fmt.Sprintf("%s=%s", hash_ev_name, suffix)
	cmd.Env = append(cmd.Env, ev)
	extraenv = append(extraenv, ev)
	for i := 0; i < len(hashes); i++ {
		ev = fmt.Sprintf("%s%d=%s", hash_ev_name, i, hashes[i])
		cmd.Env = append(cmd.Env, ev)
		extraenv = append(extraenv, ev)
	}

	if verbose {
		fmt.Fprintf(os.Stdout, "Trying %s args=%s, env=%s\n", test_command, cmd.Args, extraenv)
	} else {
		if len(extraenv) == 0 {
			fmt.Fprintf(os.Stdout, "Trying %s\n", suffix)
		} else {
			fmt.Fprintf(os.Stdout, "Trying %s\n", extraenv)
		}
	}

	if timeout <= 0 {
		output, err = cmd.CombinedOutput()
	} else {
		var b bytes.Buffer
		cmd.Stdout = &b
		cmd.Stderr = &b
		err = cmd.Start()
		if err != nil {
			return
		}
		var killErr error
		timer := time.AfterFunc(time.Second*time.Duration(timeout), func() {
			killErr = cmd.Process.Signal(os.Kill)
		})
		err = cmd.Wait()
		if killErr != nil {
			// Not sure what I would do with this,
			// and it could appear merely as the result of a lost race.
		}
		timer.Stop()
		output = b.Bytes()
	}

	if verbose {
		fmt.Fprintf(os.Stdout, "%s", string(output))
	}
	return
}

// trySuffix runs the test command passing it suffix as an argument,
// and returns PASSED/FAILED/DONE/DONE0 based on return code and occurrences
// of the function_selection_string within the output; if there is only
// one and the command fails, then the search is done.
// Appropriate log files and narrative are also produced.
func trySuffix(suffix string) int {
	function_selection_bytes := []byte(function_selection_string)
	output, error := tryCmd(suffix)
	count := bytes.Count(output, function_selection_bytes)

	if error != nil {
		// we like errors.
		fmt.Fprintf(os.Stdout, "%s failed: %s\n", test_command, error.Error())
		lfn := fmt.Sprintf("%sFAIL.%d.log", logPrefix, next_singleton_hash_index)
		saveLogFile(lfn, output)
		if count <= 1 {
			fmt.Fprintf(os.Stdout, "Review %s for failing run\n", lfn)
			if count == 0 {
				return DONE0
			}
			return DONE
		}
		return FAILED
	}
	saveLogFile(logPrefix+"PASS.log", output)
	if count == 0 {
		return PASSED0
	}
	return PASSED
}

func main() {
	flag.Var(&args, "c", "executable file of one arg hashstring to run.\n"+
		"\tMay be repeated to supply leading args to command.\n\t") // default on next line

	flag.StringVar(&logPrefix, "l", logPrefix, "prefix of log file names ending ...{PASS,FAIL}.log")
	flag.IntVar(&hashLimit, "n", hashLimit, "maximum hash string length to try before giving up")
	flag.StringVar(&suffix, "P", suffix, "root string to begin searching at (default empty)")
	flag.BoolVar(&verbose, "v", verbose, "also print output of test script (default false)")
	flag.IntVar(&timeout, "t", timeout, "timeout in seconds for running test script, 0=run till done")
	flag.BoolVar(&fail, "f", fail, "act as a test program")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr,
			`
%s runs
`,
			os.Args[0])
	}

	flag.Parse()

	if fail {
		// Be a test program instead.
		test()
		return
	}

	// Extract test command and args if supplied.
	if len(args) > 0 {
		test_command = args[0]
		args = args[1:]
	}

	rand.Seed(time.Now().UnixNano())

	// confirmed_suffix is a suffix that is confirmed
	// to contain a failure.  The first confirmation is
	// assumed to have occurred externally before this
	// program was run.
	confirmed_suffix := suffix
searchloop:
	for len(confirmed_suffix) < hashLimit {
		a := "0"
		b := "1"
		if 0 == 8192&rand.Int() {
			t := a
			a = b
			b = t
		}
		suffix = a + confirmed_suffix
		first_result := trySuffix(suffix)
		switch first_result {
		case FAILED:
			// Suffix is confirmed to contain a failure,
			// but there is more than one match (function compiled)
			// Record this confirmation and continue the search.
			confirmed_suffix = suffix
			continue

		case PASSED0:
		case PASSED:
			// Suffix does not trigger a failure, so try
			// prepending a "1" instead, below.
		case DONE0:
			// Treat this like a "pass" -- this hashcode is not useful for failure.

		case DONE:
			// suffix caused exactly one function to be optimized
			// and the test also failed.
			if next_singleton_hash_index == len(hashes) {
				// In this case all confirmed searches have yielded
				// singleton instances and we are done.
				break searchloop
			}
			// record this discovery and move on to the next one.
			confirmed_suffix = hashes[next_singleton_hash_index]
			hashes[next_singleton_hash_index] = suffix
			next_singleton_hash_index++
			continue
		}

		// The a arm contained no failures, try the b arm.
		suffix = b + confirmed_suffix
		switch trySuffix(suffix) {
		case FAILED:
			confirmed_suffix = suffix
			continue
		case PASSED:
			if first_result == PASSED {
				fmt.Fprintf(os.Stdout, "Both trials unexpectedly succeeded\n")
				// 0xyz and 1xyz both succeeded alone, but xyz failed.
				// Failure therefore requires at least 2 hits, one in
				// 0xyz and one in 1xyz.  Therefore, put 1xyz in the set
				// of confirmed (i.e., contains a non-isolated failure)
				// mark 0xyz as confirmed for local search, and continue.
				if 0 == 8192&rand.Int() {
					t := a
					a = b
					b = t
				}
				hashes = append(hashes, b+confirmed_suffix)
				confirmed_suffix = a + confirmed_suffix
				continue
			}
			fallthrough

		case PASSED0, DONE0:
			// If we are here, the test is flaky.
			fmt.Fprintf(os.Stdout, "Combination of empty and pass, discard path (test is flaky)\n")
			if next_singleton_hash_index == len(hashes) {
				break searchloop
			}
			confirmed_suffix = hashes[len(hashes)-1]
			hashes = hashes[0 : len(hashes)-1]
			continue

		case DONE:
			if next_singleton_hash_index == len(hashes) {
				break searchloop
			}
			// Randomly choose another place to work.
			j := rand.Intn(len(hashes) - next_singleton_hash_index)
			confirmed_suffix = hashes[next_singleton_hash_index+j]
			hashes[next_singleton_hash_index+j] = hashes[next_singleton_hash_index]
			hashes[next_singleton_hash_index] = suffix
			next_singleton_hash_index++
			continue
		}
	}

	fmt.Fprintf(os.Stdout, "Finished with GOSSAHASH=%s\n", string(suffix))

	// Because the tests can be flaky, see if we accidentally included hashes that aren't
	// really necessary.  This is a boring mechanical task that computers excel at...
	if len(hashes) > 0 {
		fmt.Printf("Before filtering, multiple methods required for failure:\nGOSSAHASH=%s", suffix)
		for i := 0; i < len(hashes); i++ {
			fmt.Printf(" %s%d=%s", hash_ev_name, i, hashes[i])
		}
		fmt.Println()

		// Next filter the hashes to see if any can be excluded:
		temporarily_removed := hashes[len(hashes)-1]
		hashes = hashes[0 : len(hashes)-1]
		// suffix is initially the last value of GOSSAHASH

		for i := len(hashes); i >= -1 && len(hashes) > 0; i-- {
			// Special values for search:
			// hashes[len(hashes)] == temporarily_removed,
			// hashes[-1] == suffix
			t := temporarily_removed
			if i == -1 {
				temporarily_removed = suffix
				suffix = t
			} else if i < len(hashes) {
				temporarily_removed = hashes[i]
				hashes[i] = t
			}
			switch trySuffix(suffix) {
			case DONE0: // failed but GOSSAHASH triggered nothing
				// needed neither GOSSAHASH nor the excluded one.
				if len(hashes) > 1 { // cannot be zero, see loop condition.
					temporarily_removed = ""
					suffix = hashes[len(hashes)-1]
					hashes = nil // exit with only suffix
				} else {
					suffix = hashes[len(hashes)-1]
					temporarily_removed = hashes[len(hashes)-2]
					hashes = hashes[0 : len(hashes)-2]
				}
			case DONE, FAILED: // ought not see failed, but never mind.
				temporarily_removed = hashes[len(hashes)-1]
				hashes = hashes[0 : len(hashes)-1]
			}
		}
		if temporarily_removed != "" {
			hashes = append(hashes, temporarily_removed)
		}
		fmt.Printf("After filtering, methods required for failure:\nGOSSAHASH=%s", suffix)
		for i := 0; i < len(hashes); i++ {
			fmt.Printf(" %s%d=%s", hash_ev_name, i, hashes[i])
		}
		fmt.Println()

	}
}
<|endoftext|>