<commit_before>package main

import (
	"io/ioutil"
	"bytes"
	"path/filepath"
	"strings"

	"github.com/hoisie/web"
	"github.com/xyproto/browserspeak"
)

func addHeader(page *browserspeak.Page) {
	page.metaCharset(page, "UTF-8")
	page.linkToGoogleFont(page, "Russo One")
	page.linkToGoogleFont(page, "Geostar Fill")
}

func addBodyStart(page *browserspeak.Page, bgimageurl string) {
	body, _ := page.SetMargin(4)
	repeatBackground(body, bgimageurl, "repeat-x")
	page.SetColor("gray", "#d9d9d9")
	page.SetFont("sans-serif")
}

func addLogoSearchMenu(page *browserspeak.Page, title, subtitle string) {
	body, err := page.GetTag("body")
	if err != nil {
		return
	}
	word1 := title
	word2 := ""
	if strings.Contains(title, " ") {
		word1 = strings.SplitN(title, " ", 2)[0]
		word2 = strings.SplitN(title, " ", 2)[1]
	}
	h1 := body.AddNewTag("h1")
	h1.AddStyle("font-family", "Russo One")
	a := h1.AddNewTag("a")
	a.AddAttr("href", "/")
	a.AddContent(word1)
	font := a.AddNewTag("font")
	font.AddAttr("id", "blue")
	font.AddStyle("color", "#5080D0")
	font.AddContent(word2)
	font = a.AddNewTag("font")
	font.AddAttr("id", "gray")
	font.AddStyle("font-size", "0.5em")
	font.AddStyle("color", "#808080")
	font.AddContent(subtitle)
}

func archbuilder(cssurl string) *browserspeak.Page {
	title := "Arch Linux Norway"

	page := browserspeak.NewHTML5Page(title)

	page.linkToCSS(page, cssurl)

	addHeader(page)
	addBodyStart(page, "/img/longbg4.png")
	addLogoSearchMenu(page, "Arch Linux", "Norway")

	if body, err := page.GetTag("body"); err == nil {
		h2 := body.AddNewTag("h2")
		h2.AddContent(title)
		h2.AddStyle("margin-left", "2%")
		h2.AddStyle("margin-top", "0.5em")
		h2.AddStyle("color", "black")
		h2.AddStyle("font-family", "tahoma, arial, sans-serif")
		p := body.AddNewTag("p")
		p.AddStyle("margin-left", "2%")
		p.AddStyle("margin-top", "0.5em")
		p.AddStyle("font-family", "sans-serif")
		p.AddStyle("font-size", "1.0em")
		p.AddStyle("color", "black")
	}

	return page
}

// This is a test function
func testbuilder(cssurl string) *browserspeak.Page {
	page := browserspeak.NewHTML5Page("Hello")
	body, _ := page.SetMargin(3)

	h1 := body.AddNewTag("h1")
	h1.SetMargin(1)
	h1.AddContent("Browser")

	h1, err := page.GetTag("h1")
	if err == nil {
		h1.AddContent("Spe")
	}

	if err = linkToCSS(page, cssurl); err == nil {
		h1.AddContent("ak")
	} else {
		h1.AddContent("akkkkkkkk")
	}

	page.SetColor("#202020", "#A0A0A0")
	page.SetFont("sans-serif")

	box, _ := page.AddBox("box0", true)
	box.AddStyle("margin-top", "-2em")
	box.AddStyle("margin-bottom", "3em")

	image := body.AddImage("http://www.shoutmeloud.com/wp-content/uploads/2010/01/successful-Blogger.jpeg", "50%")
	image.AddStyle("margin-top", "2em")
	image.AddStyle("margin-left", "3em")

	return page
}

func hello(val string) string {
	return browserspeak.Message("root page", "hello: "+val)
}

func main() {
	browserspeak.Publish("/test", "/main.css", testbuilder)
	browserspeak.Publish("/", "/css/style.css", archbuilder)

	// used by archbuilder
	browserspeak.PublishRootFile("img/longbg4.png")

	web.Get("/error", errorlog)

	web.Get("/hi", hi)

	web.Get("/hello/(.*)", hello)

	web.Get("/(.*)", notFound)
	web.Run("0.0.0.0:3000")
}
<commit_msg>Can now generate a webpage with custom content<commit_after>package main

import (
	"strings"

	"github.com/hoisie/web"
	"github.com/xyproto/browserspeak"
)

type ArchPageContents struct {
	generatedCSSurl string
	extraCSSurl string
	bgImageFilename string
	bgImageURL string
	title string
	subtitle string
	links []string
	contentTitle string
	contentHTML string
	searchButtonText string
	searchURL string
	footerText string
}

const (
	NICEBLUE = "#5080D0"
)

func addHeader(page *browserspeak.Page) {
	page.MetaCharset("UTF-8")
	page.LinkToGoogleFont("Russo One")
	page.LinkToGoogleFont("Geostar Fill")
}

func addBodyStyle(page *browserspeak.Page, bgimageurl string) {
	body, _ := page.SetMargin(4)
	body.RepeatBackground(bgimageurl, "repeat-x")
	page.SetColor("gray", "#d9d9d9")
	page.SetFontFamily("sans serif")
}

func addTitle(page *browserspeak.Page, title, subtitle string) {
	body, err := page.GetTag("body")
	if err != nil {
		return
	}

	// TODO: Add in a div and take parameters for x and y pos

	word1 := title
	word2 := ""
	if strings.Contains(title, " ") {
		word1 = strings.SplitN(title, " ", 2)[0]
		word2 = strings.SplitN(title, " ", 2)[1]
	}
	h1 := body.AddNewTag("h1")
	h1.AddAttr("id", "titletext")
	h1.AddStyle("font-family", "Russo One")

	a := h1.AddNewTag("a")
	a.AddAttr("id", "homelink")
	a.AddAttr("href", "/")
	a.AddContent(word1)
	a.AddStyle("color", "white")
	a.AddStyle("text-decoration", "none")

	font := a.AddNewTag("font")
	font.AddAttr("id", "bluetitle")
	font.AddStyle("color", NICEBLUE)
	font.AddContent(word2)

	font = a.AddNewTag("font")
	font.AddAttr("id", "graytitle")
	font.AddStyle("font-size", "0.5em")
	font.AddStyle("color", "#808080")
	font.AddContent(subtitle)
}

// Add a search box to the page, actionURL is the url to use as a get action, buttonText is the text on the search button
func addSearchBox(page *browserspeak.Page, actionURL string, buttonText string) {
	body, err := page.GetTag("body")
	if err != nil {
		return
	}
	div := body.AddNewTag("div")
	div.AddAttr("id", "searchbox")
	div.AddStyle("width", "25%")
	div.AddStyle("margin-left", "75%")
	div.AddStyle("position", "relative")
	div.AddStyle("top", "-3.2em")
	div.AddStyle("text-align", "right")

	form := div.AddNewTag("form")
	form.AddAttr("id", "search")
	form.AddAttr("method", "get")
	form.AddAttr("action", actionURL)

	inputText := form.AddNewTag("input")
	inputText.AddAttr("name", "q")
	inputText.AddAttr("size", "22")

	inputButton := form.AddNewTag("input")
	inputButton.AddStyle("margin-left", "0.4em")
	inputButton.AddAttr("type", "submit")
	inputButton.AddAttr("value", buttonText)
}

// Split a string at the colon into two strings
// If there's no colon, return the string and an empty string
func colonsplit(s string) (string, string) {
	if strings.Contains(s, ":") {
		sl := strings.SplitN(s, ":", 2)
		return sl[0], sl[1]
	}
	return s, ""
}

func boxStyle(tag *browserspeak.Tag) {
	tag.AddStyle("border", "solid 1px #b4b4b4")
	tag.AddStyle("border-radius", "10px")
	tag.AddStyle("box-shadow", "0 1px 3px rgba(0,0,0, .3)")
	tag.AddStyle("background-color", "#eeeeee")
}

// Takes a page and a colon-separated string slice of text:url
func addMenu(page *browserspeak.Page, links []string) {
	body, err := page.GetTag("body")
	if err != nil {
		return
	}
	div := body.AddNewTag("div")
	div.AddAttr("id", "menubox")
	div.AddStyle("margin-left", "-1em")
	div.AddStyle("margin-top", "1.5em")
	div.AddStyle("padding-top", "1em")
	div.AddStyle("padding-left", "1em")
	div.AddStyle("padding-bottom", "0.5em")
	div.AddStyle("position", "absolute")
	div.AddStyle("top", "150px")
	div.AddStyle("float", "left")

	//ul := div.AddNewTag("ul")
	//ul.AddAttr("id", "nav")

	//ul.AddStyle("margin", "0")
	//ul.AddStyle("padding", "0")
	//ul.AddStyle("width", "185px")
	//ul.AddStyle("position", "absolute")
	//ul.AddStyle("top", "35px")
	//ul.AddStyle("left", "0")
	//boxStyle(ul)

	//var li
	var a *browserspeak.Tag
	var text, url string
	//styleadded1 := false
	styleadded2 := false
	for _, text_url := range links {
		text, url = colonsplit(text_url)
		//li = ul.AddNewTag("li")
		//li.AddAttr("id", "menuitem") //strings.ToLower(text))

		//if !styleadded1 {
		//	li.AddStyle("margin", "1px 5px")
		//	li.AddStyle("padding", "0 0 8px")
		//	li.AddStyle("float", "left")
		//	li.AddStyle("position", "relative")
		//	li.AddStyle("list-style", "none")
		//	li.AddStyle("display", "inline-block")
		//	styleadded1 = true
		//}

		a = div.AddNewTag("a")
		a.AddAttr("id", "menulink")
		a.AddAttr("href", url)
		if !styleadded2 {
			a.AddStyle("font-weight", "bold")
			a.AddStyle("color", "#303030")
			a.AddStyle("text-decoration", "none")
			a.AddStyle("padding", "8px 1.2em")
			a.AddStyle("margin", "0")
			a.AddStyle("font-family", "sans serif")
			a.AddStyle("display", "block")
			a.AddStyle("width", "60px")
			styleadded2 = true
		}
		//a.AddStyle("font-size", "0.5em")
		a.AddContent(text)

	}
}

func addLogoSearchMenu(page *browserspeak.Page, title, subtitle string, links []string, searchButtonText, searchURL string) {
	addTitle(page, title, subtitle)
	addSearchBox(page, searchURL, searchButtonText)
	addMenu(page, links)
}

func addFooter(page *browserspeak.Page, footerText string) {
	body, err := page.GetTag("body")
	if err != nil {
		return
	}
	div := body.AddNewTag("div")
	div.AddAttr("id", "notice")
	div.AddStyle("position", "absolute")
	div.AddStyle("top", "90%")
	div.AddStyle("left", "90%")
	div.AddStyle("font-size", "0.7em")
	div.AddContent(footerText)
	//div.AddNewTag("br")
}

func addContent(page *browserspeak.Page, contentTitle, contentHTML string) {
	body, err := page.GetTag("body")
	if err != nil {
		return
	}
	//script := body.AddNewTag("script")
	//script.AddAttr("language", "javascript")
	//script.AddContent(`document.getElementById("overview").setAttribute("class", "current");`)

	div := body.AddNewTag("div")
	div.AddAttr("id", "content")
	div.AddStyle("z-index", "-1")
	div.AddStyle("color", "black")
	div.AddStyle("width", "85%")
	div.AddStyle("min-height", "600px")
	div.AddStyle("float", "left")
	div.AddStyle("position", "relative")
	div.AddStyle("margin-left", "150px")
	div.AddStyle("padding-left", "5em")
	div.AddStyle("padding-bottom", "0.5em")
	div.AddStyle("margin-top", "1.5em")
	div.AddStyle("padding-top", "1em")
	boxStyle(div)

	h2 := div.AddNewTag("h2")
	h2.AddAttr("id", "textheader")
	h2.AddContent(contentTitle)

	p := div.AddNewTag("p")
	p.AddAttr("id", "textparagraph")
	p.AddStyle("margin-top", "0.5em")
	p.AddStyle("font-family", "sans serif")
	p.AddStyle("font-size", "1.0em")
	p.AddStyle("color", "black")
	p.AddContent(contentHTML)
}

// TODO: Use strings like {{.Title}} instead and run it through the html.template system
func archbuilder(apc *ArchPageContents) *browserspeak.Page {
	page := browserspeak.NewHTML5Page(apc.title + " " + apc.subtitle)

	page.LinkToCSS(apc.generatedCSSurl)
	page.LinkToCSS(apc.extraCSSurl)

	addHeader(page)
	addBodyStyle(page, apc.bgImageURL)
	addLogoSearchMenu(page, apc.title, apc.subtitle, apc.links, apc.searchButtonText, apc.searchURL)
	addContent(page, apc.contentTitle, apc.contentHTML)
	addFooter(page, apc.footerText)

	return page
}

func hello(val string) string {
	return browserspeak.Message("root page", "hello: "+val)
}

func hover(ctx *web.Context) string {
	ctx.ContentType("css")
	return `
#menulink:link {color:black;}
#menulink:visited {color:black;}
#menulink:hover {color:` + NICEBLUE + `;}
#menulink:active {color:red;}
`
}

func search(ctx *web.Context, val string) string {
	q, found := ctx.Params["q"]
	if found == false {
		return browserspeak.Message("Error", "invalid params")
	}
	return browserspeak.Message("Search", q)
}

func BaseAPC() *ArchPageContents {
	var apc ArchPageContents
	apc.generatedCSSurl = "/css/style.css"
	apc.extraCSSurl = "/css/extra.css"
	apc.bgImageFilename = "img/longbg4.png"
	apc.bgImageURL = "/img/longbg4.png"
	apc.title = "Arch Linux"
	apc.subtitle = "Norway"
	apc.links = []string{"Overview:/", "List:/list", "Submit:/submit", "Ais:http://github.com/xyproto/ais", "Setconf:http://setconf.roboticoverlords.org/", "ArchFriend:https://play.google.com/store/apps/details?id=com.xyproto.archfriend"}
	apc.contentTitle = "Welcome"
	apc.contentHTML = "Hi there!"
	apc.searchButtonText = "Search"
	apc.searchURL = "/search"
	apc.footerText = "Alexander Rødseth, 2012"
	return &apc
}

func HiAPC() *ArchPageContents {
	apc := BaseAPC()
	apc.contentHTML = "Hi!"
	return apc
}

func main() {
	apc := HiAPC()
	archpage := archbuilder(apc)
	web.Get("/", browserspeak.HTML(archpage))
	web.Get(apc.generatedCSSurl, browserspeak.CSS(archpage))
	web.Get(apc.extraCSSurl, hover)
	web.Get(apc.bgImageURL, browserspeak.FILE(apc.bgImageFilename))

	web.Get(apc.searchURL + "(.*)", search)
	web.Get("/error", browserspeak.Errorlog)

	web.Get("/hello/(.*)", hello)

	web.Get("/(.*)", browserspeak.NotFound)
	web.Run("0.0.0.0:3000")
}
<|endoftext|>


<commit_before>// See cmd package.
package main

import (
	"errors"
	"github.com/spf13/cobra"
	"github.com/zrepl/zrepl/client"
	"github.com/zrepl/zrepl/config"
	"github.com/zrepl/zrepl/daemon"
	"log"
	"os"
	"fmt"
)

var rootCmd = &cobra.Command{
	Use:   "zrepl",
	Short: "ZFS dataset replication",
	Long: `Replicate ZFS filesystems & volumes between pools:

  - push & pull mode
  - automatic snapshot creation & pruning
  - local / over the network
  - ACLs instead of blank SSH access`,
}

var daemonCmd = &cobra.Command{
	Use:   "daemon",
	Short: "daemon",
	RunE: func(cmd *cobra.Command, args []string) error {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err != nil {
			return err
		}
		return daemon.Run(conf)
	},
}

var wakeupCmd = &cobra.Command{
	Use:   "wakeup",
	Short: "wake up jobs",
	RunE: func(cmd *cobra.Command, args []string) error {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err != nil {
			return err
		}
		return client.RunWakeup(conf, args)
	},
}

var statusCmdFlags client.StatusFlags

var statusCmd = &cobra.Command{
	Use:   "status",
	Short: "status",
	RunE: func(cmd *cobra.Command, args []string) error {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err != nil {
			return err
		}
		return client.RunStatus(statusCmdFlags, conf, args)
	},
}

var stdinserverCmd = &cobra.Command{
	Use:   "stdinserver CLIENT_IDENTITY",
	Short: "start in stdinserver mode (from authorized_keys file)",
	RunE: func(cmd *cobra.Command, args []string) error {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err != nil {
			return err
		}
		return client.RunStdinserver(conf, args)
	},
}

var bashcompCmd = &cobra.Command{
	Use:   "bashcomp path/to/out/file",
	Short: "generate bash completions",
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) != 1 {
			fmt.Fprintf(os.Stderr, "specify exactly one positional agument\n")
			cmd.Usage()
			os.Exit(1)
		}
		if err := rootCmd.GenBashCompletionFile(args[0]); err != nil {
			fmt.Fprintf(os.Stderr, "error generating bash completion: %s", err)
			os.Exit(1)
		}
	},
	Hidden: true,
}

var configcheckCmd = &cobra.Command{
	Use: "configcheck",
	Short: "validate config file",
	RunE: func(cmd *cobra.Command, args []string) error {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err != nil {
			return err
		}
		return client.RunConfigcheck(conf, args)
	},
}

var versionCmdArgs client.VersionArgs
var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "print version of zrepl binary (for running daemon 'zrepl control version' command)",
	Run: func(cmd *cobra.Command, args []string) {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err == nil {
			versionCmdArgs.Config = conf
		}
		client.RunVersion(versionCmdArgs)
	},
}

var pprofCmd = &cobra.Command{
	Use:   "pprof off | [on TCP_LISTEN_ADDRESS]",
	Short: "start a http server exposing go-tool-compatible profiling endpoints at TCP_LISTEN_ADDRESS",
	RunE: func(cmd *cobra.Command, args []string) error {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err != nil {
			return err
		}

		var pprofCmdArgs client.PProfArgs
		if cmd.Flags().NArg() < 1 {
			goto enargs
		}
		switch cmd.Flags().Arg(0) {
		case "on":
			pprofCmdArgs.Run = true
			if cmd.Flags().NArg() != 2 {
				return errors.New("must specify TCP_LISTEN_ADDRESS as second positional argument")
			}
			pprofCmdArgs.HttpListenAddress = cmd.Flags().Arg(1)
		case "off":
			if cmd.Flags().NArg() != 1 {
				goto enargs
			}
			pprofCmdArgs.Run = false
		}

		client.RunPProf(conf, pprofCmdArgs)
		return nil
	enargs:
		return errors.New("invalid number of positional arguments")

	},
}

var rootArgs struct {
	configFile string
}

func init() {
	//cobra.OnInitialize(initConfig)
	rootCmd.PersistentFlags().StringVar(&rootArgs.configFile, "config", "", "config file path")
	rootCmd.AddCommand(daemonCmd)
	rootCmd.AddCommand(wakeupCmd)
	statusCmd.Flags().BoolVar(&statusCmdFlags.Raw, "raw", false, "dump raw response from zrepl daemon")
	rootCmd.AddCommand(statusCmd)
	rootCmd.AddCommand(stdinserverCmd)
	rootCmd.AddCommand(bashcompCmd)
	rootCmd.AddCommand(configcheckCmd)
	versionCmd.Flags().StringVar(&versionCmdArgs.Show, "show", "", "version info to show (client|daemon)")
	rootCmd.AddCommand(versionCmd)
	rootCmd.AddCommand(pprofCmd)
}

func main() {

	if err := rootCmd.Execute(); err != nil {
		log.Printf("error executing root command: %s", err)
		os.Exit(1)
	}
}
<commit_msg>main: better descriptions for root subcommands<commit_after>// See cmd package.
package main

import (
	"errors"
	"github.com/spf13/cobra"
	"github.com/zrepl/zrepl/client"
	"github.com/zrepl/zrepl/config"
	"github.com/zrepl/zrepl/daemon"
	"log"
	"os"
	"fmt"
)

var rootCmd = &cobra.Command{
	Use:   "zrepl",
	Short: "One-stop ZFS replication solution",
}

var daemonCmd = &cobra.Command{
	Use:   "daemon",
	Short: "run the zrepl daemon",
	RunE: func(cmd *cobra.Command, args []string) error {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err != nil {
			return err
		}
		return daemon.Run(conf)
	},
}

var wakeupCmd = &cobra.Command{
	Use:   "wakeup JOB",
	Short: "trigger replication and subsequent pruning for a job",
	RunE: func(cmd *cobra.Command, args []string) error {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err != nil {
			return err
		}
		return client.RunWakeup(conf, args)
	},
}

var statusCmdFlags client.StatusFlags

var statusCmd = &cobra.Command{
	Use:   "status",
	Short: "show job activity or dump as JSON for monitoring",
	RunE: func(cmd *cobra.Command, args []string) error {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err != nil {
			return err
		}
		return client.RunStatus(statusCmdFlags, conf, args)
	},
}

var stdinserverCmd = &cobra.Command{
	Use:   "stdinserver CLIENT_IDENTITY",
	Short: "stdinserver transport mode (started from authorized_keys file as forced command)",
	RunE: func(cmd *cobra.Command, args []string) error {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err != nil {
			return err
		}
		return client.RunStdinserver(conf, args)
	},
}

var bashcompCmd = &cobra.Command{
	Use:   "bashcomp path/to/out/file",
	Short: "generate bash completions",
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) != 1 {
			fmt.Fprintf(os.Stderr, "specify exactly one positional agument\n")
			cmd.Usage()
			os.Exit(1)
		}
		if err := rootCmd.GenBashCompletionFile(args[0]); err != nil {
			fmt.Fprintf(os.Stderr, "error generating bash completion: %s", err)
			os.Exit(1)
		}
	},
	Hidden: true,
}

var configcheckCmd = &cobra.Command{
	Use: "configcheck",
	Short: "check if config can be parsed without errors",
	RunE: func(cmd *cobra.Command, args []string) error {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err != nil {
			return err
		}
		return client.RunConfigcheck(conf, args)
	},
}

var versionCmdArgs client.VersionArgs
var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "print version of zrepl binary and running daemon",
	Run: func(cmd *cobra.Command, args []string) {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err == nil {
			versionCmdArgs.Config = conf
		}
		client.RunVersion(versionCmdArgs)
	},
}

var pprofCmd = &cobra.Command{
	Use:   "pprof off | [on TCP_LISTEN_ADDRESS]",
	Short: "start a http server exposing go-tool-compatible profiling endpoints at TCP_LISTEN_ADDRESS",
	RunE: func(cmd *cobra.Command, args []string) error {
		conf, err := config.ParseConfig(rootArgs.configFile)
		if err != nil {
			return err
		}

		var pprofCmdArgs client.PProfArgs
		if cmd.Flags().NArg() < 1 {
			goto enargs
		}
		switch cmd.Flags().Arg(0) {
		case "on":
			pprofCmdArgs.Run = true
			if cmd.Flags().NArg() != 2 {
				return errors.New("must specify TCP_LISTEN_ADDRESS as second positional argument")
			}
			pprofCmdArgs.HttpListenAddress = cmd.Flags().Arg(1)
		case "off":
			if cmd.Flags().NArg() != 1 {
				goto enargs
			}
			pprofCmdArgs.Run = false
		}

		client.RunPProf(conf, pprofCmdArgs)
		return nil
	enargs:
		return errors.New("invalid number of positional arguments")

	},
}

var rootArgs struct {
	configFile string
}

func init() {
	//cobra.OnInitialize(initConfig)
	rootCmd.PersistentFlags().StringVar(&rootArgs.configFile, "config", "", "config file path")
	rootCmd.AddCommand(daemonCmd)
	rootCmd.AddCommand(wakeupCmd)
	statusCmd.Flags().BoolVar(&statusCmdFlags.Raw, "raw", false, "dump raw status description from zrepl daemon")
	rootCmd.AddCommand(statusCmd)
	rootCmd.AddCommand(stdinserverCmd)
	rootCmd.AddCommand(bashcompCmd)
	rootCmd.AddCommand(configcheckCmd)
	versionCmd.Flags().StringVar(&versionCmdArgs.Show, "show", "", "version info to show (client|daemon)")
	rootCmd.AddCommand(versionCmd)
	rootCmd.AddCommand(pprofCmd)
}

func main() {

	if err := rootCmd.Execute(); err != nil {
		log.Printf("error executing root command: %s", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
)

// VERSION ...
var VERSION string

// SourceFile ...
type SourceFile struct {
	name         string
	folder       string
	src          string
	contents     *string
	dependencies []SourceFile
}

var logger = loadLogger()

func loadLogger() *log.Logger {
	var _, debugFlag = os.LookupEnv("SCALA_SCRIPT_DEBUG")
	if debugFlag {
		return log.New(os.Stderr, "", 0)
	}

	null, _ := os.OpenFile(os.DevNull, os.O_WRONLY, 0666)
	var logger = log.New(null, "", 0)
	return logger
}

// cheap exit and error message
//
func check(e error) {
	if e != nil {
		panic(e)
	}
}

// how to use
//
func usage() {
	sysLogger := log.New(os.Stderr, "", 0)
	name := filepath.Base(os.Args[0])
	sysLogger.Println(name, VERSION)
	sysLogger.Println("usage:", name, "script.scala [script-args]")
	sysLogger.Println("usage:", name, "--repl [scala-args]")
}

// one-liner text loader
//
func fileAsText(filename string) (string, error) {
	data, err := ioutil.ReadFile(filename)
	return string(data), err
}

// one-liner text storage
//
func textToFile(filename string, content string) error {
	bytes := []byte(content)
	err := ioutil.WriteFile(filename, bytes, 0640)
	return err
}

// recursively load included file contents
//
func loadChildFile(working string, filename string) (*SourceFile, error) {
	srcName, err := filepath.Abs(filepath.Join(working, filename))
	if err != nil {
		return nil, err
	}
	result := SourceFile{}
	result.name = filename
	result.folder = filepath.Dir(srcName)
	result.src = filepath.Base(srcName)

	// load the contents
	//
	contents, err := fileAsText(filepath.Join(result.folder, result.src))
	if err != nil {
		return nil, err
	}

	// scan through to get the list of files we need
	//
	re := regexp.MustCompile(`(?m)(//#include)(\W+)(.*)\n`)
	for _, match := range re.FindAllStringSubmatch(contents, -1) {
		childName := match[3]
		child, err := loadChildFile(result.folder, childName)
		if err != nil {
			return nil, err
		}
		result.dependencies = append(result.dependencies, *child)
	}

	// go through once more to inject the contents
	//
	for _, child := range result.dependencies {
		rer := regexp.MustCompile(`//#include\W+` + child.name)
		contents = rer.ReplaceAllLiteralString(contents, *child.contents)
	}

	result.contents = &contents

	return &result, nil
}

// root script file loader
//
func loadFile(working string, filename string) (*SourceFile, string, error) {

	result, err := loadChildFile(working, filename)
	if err != nil {
		return nil, "", err
	}
	destName := ".g." + result.src

	var changed bool
	outFile := filepath.Join(result.folder, destName)

	// get timestamp off the source file
	//
	ss, err := os.Stat(filepath.Join(result.folder, result.src))
	if err != nil {
		return nil, "", err
	}

	// dest file is optional
	//
	ds, err := os.Stat(outFile)
	if err != nil {
		// either it does not exist or some other reason we would want to overwrite it
		//
		changed = true
		logger.Println("no destination exists")
	} else if ds != nil {
		changed = ss.ModTime().After(ds.ModTime())
		if !changed {
			// final check is a content check against the generated content
			//
			otherContent, err := fileAsText(outFile)
			if err != nil {
				return nil, "", err
			}
			changed = otherContent != *result.contents
			if changed {
				logger.Println("content difference")
			}
		} else {
			logger.Println("timestamp difference")
		}
	}

	if changed {
		logger.Println("saving file")
		err = textToFile(outFile, *result.contents)
		check(err)
	}

	return result, outFile, nil
}

// class path builder
//
func gatherClassPath(root string) string {
	logger.Println("class path root:", root)
	files, err := ioutil.ReadDir(filepath.Join(root, "lib"))
	if err != nil {
		return ""
	}

	var cp []string
	for i := range files {
		f := files[i]
		if filepath.Ext(f.Name()) == ".jar" {
			name := filepath.Join(root, "lib", f.Name())
			cp = append(cp, name)
		}
	}

	return strings.Join(cp, ":")
}

// entry point
//
func main() {
	args := os.Args[1:]

	if len(args) <= 0 {
		usage()
		os.Exit(1)
	}

	wd, err := os.Getwd()
	check(err)

	var cp string
	var result *SourceFile
	var scriptFile string

	if args[0] == "--repl" {
		cp = gatherClassPath(wd)
		scriptFile = wd
	} else {
		result, scriptFile, err = loadFile(wd, args[0])
		check(err)

		cp = gatherClassPath(result.folder)
	}

	cmdFile, err := exec.LookPath("scala")
	check(err)

	env := os.Environ()
	launchArgs := []string{
		cmdFile,
		"-deprecation",
		"-feature",
		"-savecompiled",
		"-classpath",
		cp,
		"-Dscala.script.name=" + scriptFile}

	// first arg is either --repl or script to execute
	//
	if result != nil {
		launchArgs = append(launchArgs, scriptFile)
	}
	args = args[1:]
	launchArgs = append(launchArgs, args...)

	// spawn the call, replacing ourselves
	//
	logger.Println("command:", launchArgs)
	err = syscall.Exec(cmdFile, launchArgs, env)
	check(err)
}
<commit_msg>Adding root folder for classpath resource loading<commit_after>package main

import (
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
)

// VERSION ...
var VERSION string

// SourceFile ...
type SourceFile struct {
	name         string
	folder       string
	src          string
	contents     *string
	dependencies []SourceFile
}

var logger = loadLogger()

func loadLogger() *log.Logger {
	var _, debugFlag = os.LookupEnv("SCALA_SCRIPT_DEBUG")
	if debugFlag {
		return log.New(os.Stderr, "", 0)
	}

	null, _ := os.OpenFile(os.DevNull, os.O_WRONLY, 0666)
	var logger = log.New(null, "", 0)
	return logger
}

// cheap exit and error message
//
func check(e error) {
	if e != nil {
		panic(e)
	}
}

// how to use
//
func usage() {
	sysLogger := log.New(os.Stderr, "", 0)
	name := filepath.Base(os.Args[0])
	sysLogger.Println(name, VERSION)
	sysLogger.Println("usage:", name, "script.scala [script-args]")
	sysLogger.Println("usage:", name, "--repl [scala-args]")
}

// one-liner text loader
//
func fileAsText(filename string) (string, error) {
	data, err := ioutil.ReadFile(filename)
	return string(data), err
}

// one-liner text storage
//
func textToFile(filename string, content string) error {
	bytes := []byte(content)
	err := ioutil.WriteFile(filename, bytes, 0640)
	return err
}

// recursively load included file contents
//
func loadChildFile(working string, filename string) (*SourceFile, error) {
	srcName, err := filepath.Abs(filepath.Join(working, filename))
	if err != nil {
		return nil, err
	}
	result := SourceFile{}
	result.name = filename
	result.folder = filepath.Dir(srcName)
	result.src = filepath.Base(srcName)

	// load the contents
	//
	contents, err := fileAsText(filepath.Join(result.folder, result.src))
	if err != nil {
		return nil, err
	}

	// scan through to get the list of files we need
	//
	re := regexp.MustCompile(`(?m)(//#include)(\W+)(.*)\n`)
	for _, match := range re.FindAllStringSubmatch(contents, -1) {
		childName := match[3]
		child, err := loadChildFile(result.folder, childName)
		if err != nil {
			return nil, err
		}
		result.dependencies = append(result.dependencies, *child)
	}

	// go through once more to inject the contents
	//
	for _, child := range result.dependencies {
		rer := regexp.MustCompile(`//#include\W+` + child.name)
		contents = rer.ReplaceAllLiteralString(contents, *child.contents)
	}

	result.contents = &contents

	return &result, nil
}

// root script file loader
//
func loadFile(working string, filename string) (*SourceFile, string, error) {

	result, err := loadChildFile(working, filename)
	if err != nil {
		return nil, "", err
	}
	destName := ".g." + result.src

	var changed bool
	outFile := filepath.Join(result.folder, destName)

	// get timestamp off the source file
	//
	ss, err := os.Stat(filepath.Join(result.folder, result.src))
	if err != nil {
		return nil, "", err
	}

	// dest file is optional
	//
	ds, err := os.Stat(outFile)
	if err != nil {
		// either it does not exist or some other reason we would want to overwrite it
		//
		changed = true
		logger.Println("no destination exists")
	} else if ds != nil {
		changed = ss.ModTime().After(ds.ModTime())
		if !changed {
			// final check is a content check against the generated content
			//
			otherContent, err := fileAsText(outFile)
			if err != nil {
				return nil, "", err
			}
			changed = otherContent != *result.contents
			if changed {
				logger.Println("content difference")
			}
		} else {
			logger.Println("timestamp difference")
		}
	}

	if changed {
		logger.Println("saving file")
		err = textToFile(outFile, *result.contents)
		check(err)
	}

	return result, outFile, nil
}

// class path builder
//
func gatherClassPath(root string) string {
	logger.Println("class path root:", root)
	files, err := ioutil.ReadDir(filepath.Join(root, "lib"))
	if err != nil {
		return ""
	}

	var cp []string
	for i := range files {
		f := files[i]
		if filepath.Ext(f.Name()) == ".jar" {
			name := filepath.Join(root, "lib", f.Name())
			cp = append(cp, name)
		}
	}
	cp = append(cp, root)

	return strings.Join(cp, ":")
}

// entry point
//
func main() {
	args := os.Args[1:]

	if len(args) <= 0 {
		usage()
		os.Exit(1)
	}

	wd, err := os.Getwd()
	check(err)

	var cp string
	var result *SourceFile
	var scriptFile string

	if args[0] == "--repl" {
		cp = gatherClassPath(wd)
		scriptFile = wd
	} else {
		result, scriptFile, err = loadFile(wd, args[0])
		check(err)

		cp = gatherClassPath(result.folder)
	}

	cmdFile, err := exec.LookPath("scala")
	check(err)

	env := os.Environ()
	launchArgs := []string{
		cmdFile,
		"-deprecation",
		"-feature",
		"-savecompiled",
		"-classpath",
		cp,
		"-Dscala.script.name=" + scriptFile}

	// first arg is either --repl or script to execute
	//
	if result != nil {
		launchArgs = append(launchArgs, scriptFile)
	}
	args = args[1:]
	launchArgs = append(launchArgs, args...)

	// spawn the call, replacing ourselves
	//
	logger.Println("command:", launchArgs)
	err = syscall.Exec(cmdFile, launchArgs, env)
	check(err)
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"os"
	"path/filepath"
	"runtime"

	"github.com/cuigh/auxo/app"
	"github.com/cuigh/auxo/app/flag"
	_ "github.com/cuigh/auxo/cache/memory"
	"github.com/cuigh/auxo/config"
	"github.com/cuigh/auxo/data/valid"
	"github.com/cuigh/auxo/log"
	"github.com/cuigh/auxo/net/web"
	"github.com/cuigh/auxo/net/web/filter"
	"github.com/cuigh/auxo/net/web/filter/auth"
	"github.com/cuigh/auxo/net/web/renderer/jet"
	"github.com/cuigh/swirl/biz"
	"github.com/cuigh/swirl/controller"
	"github.com/cuigh/swirl/misc"
	"github.com/cuigh/swirl/model"
	"github.com/cuigh/swirl/scaler"
	"github.com/cuigh/swirl/security"
)

func main() {
	misc.BindOptions()

	app.Name = "Swirl"
	app.Version = "0.7.3"
	app.Desc = "A web management UI for Docker, focused on swarm cluster"
	app.Action = func(ctx *app.Context) {
		err := config.UnmarshalOption("swirl", &misc.Options)
		if err != nil {
			log.Get(app.Name).Error("Load options failed: ", err)
			os.Exit(1)
		}

		setting, err := biz.Setting.Get()
		if err != nil {
			log.Get(app.Name).Error("Load setting failed: ", err)
			os.Exit(1)
		}

		scaler.Start()
		app.Run(server(setting))
	}
	app.Flags.Register(flag.All)
	app.Start()
}

func server(setting *model.Setting) *web.Server {
	ws := web.Auto()

	// customize error handler
	ws.ErrorHandler.OnCode(http.StatusNotFound, func(ctx web.Context, err error) {
		if ctx.IsAJAX() {
			ctx.Status(http.StatusNotFound).HTML(http.StatusText(http.StatusNotFound)) // nolint: gas
		} else {
			ctx.Status(http.StatusNotFound).Render("404", nil) // nolint: gas
		}
	})
	ws.ErrorHandler.OnCode(http.StatusForbidden, func(ctx web.Context, err error) {
		if ctx.IsAJAX() {
			ctx.Status(http.StatusForbidden).HTML("You do not have permission to perform this operation") // nolint: gas
		} else {
			ctx.Status(http.StatusForbidden).Render("403", nil) // nolint: gas
		}
	})

	// set render
	ws.Validator = &valid.Validator{Tag: "valid"}
	ws.Renderer = jet.Must(jet.Debug(config.GetBool("debug")), jet.VarMap(misc.Funcs), jet.VarMap(map[string]interface{}{
		"language":   setting.Language,
		"version":    app.Version,
		"go_version": runtime.Version(),
		"time":       misc.FormatTime(setting.TimeZone.Offset),
		"i18n":       misc.Message(setting.Language),
	}))

	// register global filters
	ws.Use(filter.NewRecover())

	// register static handlers
	ws.File("/favicon.ico", filepath.Join(filepath.Dir(app.Path()), "assets/swirl/img/favicon.ico"))
	ws.Static("/assets", filepath.Join(filepath.Dir(app.Path()), "assets"))

	// create biz group
	form := &auth.Form{
		Identifier:        security.Identifier,
		Timeout:           misc.Options.AuthTimeout,
		SlidingExpiration: true,
	}
	g := ws.Group("", form, filter.NewAuthorizer(security.Checker))

	// register auth handlers
	g.Post("/login", form.LoginJSON(security.Validator(setting)), web.WithName("login"), web.WithAuthorize(web.AuthAnonymous))
	g.Get("/logout", form.Logout, web.WithName("logout"), web.WithAuthorize(web.AuthAuthenticated))

	// register controllers
	g.Handle("", controller.Home())
	g.Handle("/profile", controller.Profile())
	g.Handle("/registry", controller.Registry())
	g.Handle("/node", controller.Node())
	g.Handle("/service", controller.Service(), web.FilterFunc(security.Permiter))
	g.Handle("/service/template", controller.Template())
	g.Handle("/stack", controller.Stack())
	g.Handle("/network", controller.Network())
	g.Handle("/secret", controller.Secret())
	g.Handle("/config", controller.Config())
	g.Handle("/task", controller.Task())
	g.Handle("/container", controller.Container())
	g.Handle("/image", controller.Image())
	g.Handle("/volume", controller.Volume())
	g.Handle("/system/user", controller.User())
	g.Handle("/system/role", controller.Role())
	g.Handle("/system/setting", controller.Setting())
	g.Handle("/system/event", controller.Event())
	g.Handle("/system/chart", controller.Chart())

	return ws
}
<commit_msg>Bump version to 0.7.4<commit_after>package main

import (
	"net/http"
	"os"
	"path/filepath"
	"runtime"

	"github.com/cuigh/auxo/app"
	"github.com/cuigh/auxo/app/flag"
	_ "github.com/cuigh/auxo/cache/memory"
	"github.com/cuigh/auxo/config"
	"github.com/cuigh/auxo/data/valid"
	"github.com/cuigh/auxo/log"
	"github.com/cuigh/auxo/net/web"
	"github.com/cuigh/auxo/net/web/filter"
	"github.com/cuigh/auxo/net/web/filter/auth"
	"github.com/cuigh/auxo/net/web/renderer/jet"
	"github.com/cuigh/swirl/biz"
	"github.com/cuigh/swirl/controller"
	"github.com/cuigh/swirl/misc"
	"github.com/cuigh/swirl/model"
	"github.com/cuigh/swirl/scaler"
	"github.com/cuigh/swirl/security"
)

func main() {
	misc.BindOptions()

	app.Name = "Swirl"
	app.Version = "0.7.4"
	app.Desc = "A web management UI for Docker, focused on swarm cluster"
	app.Action = func(ctx *app.Context) {
		err := config.UnmarshalOption("swirl", &misc.Options)
		if err != nil {
			log.Get(app.Name).Error("Load options failed: ", err)
			os.Exit(1)
		}

		setting, err := biz.Setting.Get()
		if err != nil {
			log.Get(app.Name).Error("Load setting failed: ", err)
			os.Exit(1)
		}

		scaler.Start()
		app.Run(server(setting))
	}
	app.Flags.Register(flag.All)
	app.Start()
}

func server(setting *model.Setting) *web.Server {
	ws := web.Auto()

	// customize error handler
	ws.ErrorHandler.OnCode(http.StatusNotFound, func(ctx web.Context, err error) {
		if ctx.IsAJAX() {
			ctx.Status(http.StatusNotFound).HTML(http.StatusText(http.StatusNotFound)) // nolint: gas
		} else {
			ctx.Status(http.StatusNotFound).Render("404", nil) // nolint: gas
		}
	})
	ws.ErrorHandler.OnCode(http.StatusForbidden, func(ctx web.Context, err error) {
		if ctx.IsAJAX() {
			ctx.Status(http.StatusForbidden).HTML("You do not have permission to perform this operation") // nolint: gas
		} else {
			ctx.Status(http.StatusForbidden).Render("403", nil) // nolint: gas
		}
	})

	// set render
	ws.Validator = &valid.Validator{Tag: "valid"}
	ws.Renderer = jet.Must(jet.Debug(config.GetBool("debug")), jet.VarMap(misc.Funcs), jet.VarMap(map[string]interface{}{
		"language":   setting.Language,
		"version":    app.Version,
		"go_version": runtime.Version(),
		"time":       misc.FormatTime(setting.TimeZone.Offset),
		"i18n":       misc.Message(setting.Language),
	}))

	// register global filters
	ws.Use(filter.NewRecover())

	// register static handlers
	ws.File("/favicon.ico", filepath.Join(filepath.Dir(app.Path()), "assets/swirl/img/favicon.ico"))
	ws.Static("/assets", filepath.Join(filepath.Dir(app.Path()), "assets"))

	// create biz group
	form := &auth.Form{
		Identifier:        security.Identifier,
		Timeout:           misc.Options.AuthTimeout,
		SlidingExpiration: true,
	}
	g := ws.Group("", form, filter.NewAuthorizer(security.Checker))

	// register auth handlers
	g.Post("/login", form.LoginJSON(security.Validator(setting)), web.WithName("login"), web.WithAuthorize(web.AuthAnonymous))
	g.Get("/logout", form.Logout, web.WithName("logout"), web.WithAuthorize(web.AuthAuthenticated))

	// register controllers
	g.Handle("", controller.Home())
	g.Handle("/profile", controller.Profile())
	g.Handle("/registry", controller.Registry())
	g.Handle("/node", controller.Node())
	g.Handle("/service", controller.Service(), web.FilterFunc(security.Permiter))
	g.Handle("/service/template", controller.Template())
	g.Handle("/stack", controller.Stack())
	g.Handle("/network", controller.Network())
	g.Handle("/secret", controller.Secret())
	g.Handle("/config", controller.Config())
	g.Handle("/task", controller.Task())
	g.Handle("/container", controller.Container())
	g.Handle("/image", controller.Image())
	g.Handle("/volume", controller.Volume())
	g.Handle("/system/user", controller.User())
	g.Handle("/system/role", controller.Role())
	g.Handle("/system/setting", controller.Setting())
	g.Handle("/system/event", controller.Event())
	g.Handle("/system/chart", controller.Chart())

	return ws
}
<|endoftext|>


<commit_before>package main // import "github.com/inCaller/prometheus_bot"

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-telegram-bot-api/telegram-bot-api"
	"gopkg.in/yaml.v2"

	"text/template"
)

type Alerts struct {
	Alerts            []Alert                `json:"alerts"`
	CommonAnnotations map[string]interface{} `json:"commonAnnotations"`
	CommonLabels      map[string]interface{} `json:"commonLabels"`
	ExternalURL       string                 `json:"externalURL"`
	GroupKey          int                    `json:"groupKey"`
	GroupLabels       map[string]interface{} `json:"groupLabels"`
	Receiver          string                 `json:"receiver"`
	Status            string                 `json:"status"`
	Version           int                    `json:"version"`
}

type Alert struct {
	Annotations  map[string]interface{} `json:"annotations"`
	EndsAt       string                 `json:"sendsAt"`
	GeneratorURL string                 `json:"generatorURL"`
	Labels       map[string]interface{} `json:"labels"`
	StartsAt     string                 `json:"startsAt"`
}

type Config struct {
	TelegramToken string `yaml:"telegram_token"`
	TemplatePath  string `yaml:"template_path"`
	TimeZone      string `yaml:"time_zone"`
}

// Global
var config_path = flag.String("c", "config.yaml", "Path to a config file")
var listen_addr = flag.String("l", ":9087", "Listen address")
var template_path = flag.String("t", "", "Path to a template file")
var debug = flag.Bool("d", false, "Debug template")

var cfg = Config{}
var bot *tgbotapi.BotAPI
var tmpH *template.Template

// Template addictional functions map
var funcMap = template.FuncMap{
	"FormatDate": func(toformat string) string {
		IN_layout := "2006-01-02T15:04:05.000-07:00"
		OUT_layout := "02/01/2006 15:04:05"

		t, err := time.Parse(IN_layout, toformat)

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

		loc, _ := time.LoadLocation(cfg.TimeZone)

		return t.In(loc).Format(OUT_layout)
	},
}

func telegramBot(bot *tgbotapi.BotAPI) {
	u := tgbotapi.NewUpdate(0)
	u.Timeout = 60

	updates, err := bot.GetUpdatesChan(u)
	if err != nil {
		log.Fatal(err)
	}

	for update := range updates {
		if update.Message.NewChatMember != nil {
			if update.Message.NewChatMember.UserName == bot.Self.UserName && update.Message.Chat.Type == "group" {
				msg := tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("Chat id is '%d'", update.Message.Chat.ID))
				bot.Send(msg)
			}
		}
	}
}

func loadTemplate(tmplPath string) *template.Template {
	// let's read template
	tmpH, err := template.New(path.Base(tmplPath)).Funcs(funcMap).ParseFiles(cfg.TemplatePath)

	if err != nil {
		log.Fatalf("Problem reading parsing template file: %v", err)
	} else {
		log.Printf("Load template file:%s", tmplPath)
	}

	return tmpH
}

func main() {
	flag.Parse()

	content, err := ioutil.ReadFile(*config_path)
	if err != nil {
		log.Fatalf("Problem reading configuration file: %v", err)
	}
	err = yaml.Unmarshal(content, &cfg)
	if err != nil {
		log.Fatalf("Error parsing configuration file: %v", err)
	}

	if *template_path != "" {
		cfg.TemplatePath = *template_path
	}

	bot_tmp, err := tgbotapi.NewBotAPI(cfg.TelegramToken)
	if err != nil {
		log.Fatal(err)
	}

	bot = bot_tmp
	if cfg.TemplatePath != "" {

		tmpH = loadTemplate(cfg.TemplatePath)

		if cfg.TimeZone == "" {
			log.Fatalf("You must define time_zone of your bot")
			panic(-1)
		}

	} else {
		*debug = false
		tmpH = nil
	}
	//bot.Debug = true

	log.Printf("Authorized on account %s", bot.Self.UserName)

	go telegramBot(bot)

	router := gin.Default()

	router.GET("/ping/:chatid", GET_Handling)
	router.POST("/alert/:chatid", POST_Handling)
	router.Run(*listen_addr)
}

func GET_Handling(c *gin.Context) {
	log.Printf("Recived GET")
	chatid, err := strconv.ParseInt(c.Param("chatid"), 10, 64)
	if err != nil {
		log.Printf("Cat't parse chat id: %q", c.Param("chatid"))
		c.JSON(http.StatusServiceUnavailable, gin.H{
			"err": fmt.Sprint(err),
		})
		return
	}

	log.Printf("Bot test: %d", chatid)
	msgtext := fmt.Sprintf("Some HTTP triggered notification by prometheus bot... %d", chatid)
	msg := tgbotapi.NewMessage(chatid, msgtext)
	sendmsg, err := bot.Send(msg)
	if err == nil {
		c.String(http.StatusOK, msgtext)
	} else {
		c.JSON(http.StatusBadRequest, gin.H{
			"err":     fmt.Sprint(err),
			"message": sendmsg,
		})
	}
}

func AlertFormatStandard(alerts Alerts) string {
	keys := make([]string, 0, len(alerts.GroupLabels))
	for k := range alerts.GroupLabels {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	groupLabels := make([]string, 0, len(alerts.GroupLabels))
	for _, k := range keys {
		groupLabels = append(groupLabels, fmt.Sprintf("%s=%s", k, alerts.GroupLabels[k]))
	}

	keys = make([]string, 0, len(alerts.CommonLabels))
	for k := range alerts.CommonLabels {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	commonLabels := make([]string, 0, len(alerts.CommonLabels))
	for _, k := range keys {
		if _, ok := alerts.GroupLabels[k]; !ok {
			commonLabels = append(commonLabels, fmt.Sprintf("%s=%s", k, alerts.CommonLabels[k]))
		}
	}

	keys = make([]string, 0, len(alerts.CommonAnnotations))
	for k := range alerts.CommonAnnotations {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	commonAnnotations := make([]string, 0, len(alerts.CommonAnnotations))
	for _, k := range keys {
		commonAnnotations = append(commonAnnotations, fmt.Sprintf("\n%s: %s", k, alerts.CommonAnnotations[k]))
	}

	alertDetails := make([]string, len(alerts.Alerts))
	for i, a := range alerts.Alerts {
		if instance, ok := a.Labels["instance"]; ok {
			instanceString, _ := instance.(string)
			alertDetails[i] += strings.Split(instanceString, ":")[0]
		}
		if job, ok := a.Labels["job"]; ok {
			alertDetails[i] += fmt.Sprintf("[%s]", job)
		}
		if a.GeneratorURL != "" {
			alertDetails[i] = fmt.Sprintf("%s", a.GeneratorURL, alertDetails[i])
		}
	}
	return fmt.Sprintf(
		"[%s:%d]\ngrouped by: %s\nlabels: %s%s\n%s",
		alerts.ExternalURL,
		alerts.Receiver,
		strings.ToUpper(alerts.Status),
		len(alerts.Alerts),
		strings.Join(groupLabels, ", "),
		strings.Join(commonLabels, ", "),
		strings.Join(commonAnnotations, ""),
		strings.Join(alertDetails, ", "),
	)
}

func AlertFormatTemplate(alerts Alerts) string {
	var bytesBuff bytes.Buffer
	var err error

	writer := io.Writer(&bytesBuff)

	if *debug {
		log.Printf("Reloading Template\n")
		// reload template bacause we in debug mode
		tmpH = loadTemplate(cfg.TemplatePath)
	}

	tmpH.Funcs(funcMap)
	err = tmpH.Execute(writer, alerts)

	if err != nil {
		log.Fatalf("Problem with template execution: %v", err)
		panic(err)
	}

	return bytesBuff.String()
}

func POST_Handling(c *gin.Context) {
	var msgtext string
	var alerts Alerts

	chatid, err := strconv.ParseInt(c.Param("chatid"), 10, 64)

	log.Printf("Bot alert post: %d", chatid)

	if err != nil {
		log.Printf("Cat't parse chat id: %q", c.Param("chatid"))
		c.JSON(http.StatusServiceUnavailable, gin.H{
			"err": fmt.Sprint(err),
		})
		return
	}

	binding.JSON.Bind(c.Request, &alerts)

	s, err := json.Marshal(alerts)
	if err != nil {
		log.Print(err)
		return
	}

	log.Println("+------------------  A L E R T  J S O N  -------------------+")
	log.Printf("%s", s)
	log.Println("+-----------------------------------------------------------+\n\n")

	// Decide how format Text
	if cfg.TemplatePath == "" {
		msgtext = AlertFormatStandard(alerts)
	} else {
		msgtext = AlertFormatTemplate(alerts)
	}
	// Print in Log result message
	log.Println("+---------------  F I N A L   M E S S A G E  ---------------+")
	log.Println(msgtext)
	log.Println("+-----------------------------------------------------------+")

	msg := tgbotapi.NewMessage(chatid, msgtext)
	msg.ParseMode = tgbotapi.ModeHTML

	msg.DisableWebPagePreview = true

	sendmsg, err := bot.Send(msg)
	if err == nil {
		c.String(http.StatusOK, "telegram msg sent.")
	} else {
		log.Printf("Error sending message: %s", err)
		c.JSON(http.StatusServiceUnavailable, gin.H{
			"err":     fmt.Sprint(err),
			"message": sendmsg,
			"srcmsg":  fmt.Sprint(msgtext),
		})
		msg := tgbotapi.NewMessage(chatid, "Error sending message, checkout logs")
		bot.Send(msg)
	}
}
<commit_msg>https://github.com/inCaller/prometheus_bot/issues/13 fix crash on <> symbols<commit_after>package main // import "github.com/inCaller/prometheus_bot"

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-telegram-bot-api/telegram-bot-api"
	"gopkg.in/yaml.v2"

	"html/template"
)

type Alerts struct {
	Alerts            []Alert                `json:"alerts"`
	CommonAnnotations map[string]interface{} `json:"commonAnnotations"`
	CommonLabels      map[string]interface{} `json:"commonLabels"`
	ExternalURL       string                 `json:"externalURL"`
	GroupKey          int                    `json:"groupKey"`
	GroupLabels       map[string]interface{} `json:"groupLabels"`
	Receiver          string                 `json:"receiver"`
	Status            string                 `json:"status"`
	Version           int                    `json:"version"`
}

type Alert struct {
	Annotations  map[string]interface{} `json:"annotations"`
	EndsAt       string                 `json:"sendsAt"`
	GeneratorURL string                 `json:"generatorURL"`
	Labels       map[string]interface{} `json:"labels"`
	StartsAt     string                 `json:"startsAt"`
}

type Config struct {
	TelegramToken string `yaml:"telegram_token"`
	TemplatePath  string `yaml:"template_path"`
	TimeZone      string `yaml:"time_zone"`
}

// Global
var config_path = flag.String("c", "config.yaml", "Path to a config file")
var listen_addr = flag.String("l", ":9087", "Listen address")
var template_path = flag.String("t", "", "Path to a template file")
var debug = flag.Bool("d", false, "Debug template")

var cfg = Config{}
var bot *tgbotapi.BotAPI
var tmpH *template.Template

// Template addictional functions map
var funcMap = template.FuncMap{
	"FormatDate": func(toformat string) string {
		IN_layout := "2006-01-02T15:04:05.000-07:00"
		OUT_layout := "02/01/2006 15:04:05"

		t, err := time.Parse(IN_layout, toformat)

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

		loc, _ := time.LoadLocation(cfg.TimeZone)

		return t.In(loc).Format(OUT_layout)
	},
}

func telegramBot(bot *tgbotapi.BotAPI) {
	u := tgbotapi.NewUpdate(0)
	u.Timeout = 60

	updates, err := bot.GetUpdatesChan(u)
	if err != nil {
		log.Fatal(err)
	}

	for update := range updates {
		if update.Message.NewChatMember != nil {
			if update.Message.NewChatMember.UserName == bot.Self.UserName && update.Message.Chat.Type == "group" {
				msg := tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("Chat id is '%d'", update.Message.Chat.ID))
				bot.Send(msg)
			}
		}
	}
}

func loadTemplate(tmplPath string) *template.Template {
	// let's read template
	tmpH, err := template.New(path.Base(tmplPath)).Funcs(funcMap).ParseFiles(cfg.TemplatePath)

	if err != nil {
		log.Fatalf("Problem reading parsing template file: %v", err)
	} else {
		log.Printf("Load template file:%s", tmplPath)
	}

	return tmpH
}

func main() {
	flag.Parse()

	content, err := ioutil.ReadFile(*config_path)
	if err != nil {
		log.Fatalf("Problem reading configuration file: %v", err)
	}
	err = yaml.Unmarshal(content, &cfg)
	if err != nil {
		log.Fatalf("Error parsing configuration file: %v", err)
	}

	if *template_path != "" {
		cfg.TemplatePath = *template_path
	}

	bot_tmp, err := tgbotapi.NewBotAPI(cfg.TelegramToken)
	if err != nil {
		log.Fatal(err)
	}

	bot = bot_tmp
	if cfg.TemplatePath != "" {

		tmpH = loadTemplate(cfg.TemplatePath)

		if cfg.TimeZone == "" {
			log.Fatalf("You must define time_zone of your bot")
			panic(-1)
		}

	} else {
		*debug = false
		tmpH = nil
	}
	//bot.Debug = true

	log.Printf("Authorized on account %s", bot.Self.UserName)

	go telegramBot(bot)

	router := gin.Default()

	router.GET("/ping/:chatid", GET_Handling)
	router.POST("/alert/:chatid", POST_Handling)
	router.Run(*listen_addr)
}

func GET_Handling(c *gin.Context) {
	log.Printf("Recived GET")
	chatid, err := strconv.ParseInt(c.Param("chatid"), 10, 64)
	if err != nil {
		log.Printf("Cat't parse chat id: %q", c.Param("chatid"))
		c.JSON(http.StatusServiceUnavailable, gin.H{
			"err": fmt.Sprint(err),
		})
		return
	}

	log.Printf("Bot test: %d", chatid)
	msgtext := fmt.Sprintf("Some HTTP triggered notification by prometheus bot... %d", chatid)
	msg := tgbotapi.NewMessage(chatid, msgtext)
	sendmsg, err := bot.Send(msg)
	if err == nil {
		c.String(http.StatusOK, msgtext)
	} else {
		c.JSON(http.StatusBadRequest, gin.H{
			"err":     fmt.Sprint(err),
			"message": sendmsg,
		})
	}
}

func AlertFormatStandard(alerts Alerts) string {
	keys := make([]string, 0, len(alerts.GroupLabels))
	for k := range alerts.GroupLabels {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	groupLabels := make([]string, 0, len(alerts.GroupLabels))
	for _, k := range keys {
		groupLabels = append(groupLabels, fmt.Sprintf("%s=%s", k, alerts.GroupLabels[k]))
	}

	keys = make([]string, 0, len(alerts.CommonLabels))
	for k := range alerts.CommonLabels {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	commonLabels := make([]string, 0, len(alerts.CommonLabels))
	for _, k := range keys {
		if _, ok := alerts.GroupLabels[k]; !ok {
			commonLabels = append(commonLabels, fmt.Sprintf("%s=%s", k, alerts.CommonLabels[k]))
		}
	}

	keys = make([]string, 0, len(alerts.CommonAnnotations))
	for k := range alerts.CommonAnnotations {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	commonAnnotations := make([]string, 0, len(alerts.CommonAnnotations))
	for _, k := range keys {
		commonAnnotations = append(commonAnnotations, fmt.Sprintf("\n%s: %s", k, alerts.CommonAnnotations[k]))
	}

	alertDetails := make([]string, len(alerts.Alerts))
	for i, a := range alerts.Alerts {
		if instance, ok := a.Labels["instance"]; ok {
			instanceString, _ := instance.(string)
			alertDetails[i] += strings.Split(instanceString, ":")[0]
		}
		if job, ok := a.Labels["job"]; ok {
			alertDetails[i] += fmt.Sprintf("[%s]", job)
		}
		if a.GeneratorURL != "" {
			alertDetails[i] = fmt.Sprintf("%s", a.GeneratorURL, alertDetails[i])
		}
	}
	return fmt.Sprintf(
		"[%s:%d]\ngrouped by: %s\nlabels: %s%s\n%s",
		alerts.ExternalURL,
		alerts.Receiver,
		strings.ToUpper(alerts.Status),
		len(alerts.Alerts),
		strings.Join(groupLabels, ", "),
		strings.Join(commonLabels, ", "),
		strings.Join(commonAnnotations, ""),
		strings.Join(alertDetails, ", "),
	)
}

func AlertFormatTemplate(alerts Alerts) string {
	var bytesBuff bytes.Buffer
	var err error

	writer := io.Writer(&bytesBuff)

	if *debug {
		log.Printf("Reloading Template\n")
		// reload template bacause we in debug mode
		tmpH = loadTemplate(cfg.TemplatePath)
	}

	tmpH.Funcs(funcMap)
	err = tmpH.Execute(writer, alerts)

	if err != nil {
		log.Fatalf("Problem with template execution: %v", err)
		panic(err)
	}

	return bytesBuff.String()
}

func POST_Handling(c *gin.Context) {
	var msgtext string
	var alerts Alerts

	chatid, err := strconv.ParseInt(c.Param("chatid"), 10, 64)

	log.Printf("Bot alert post: %d", chatid)

	if err != nil {
		log.Printf("Cat't parse chat id: %q", c.Param("chatid"))
		c.JSON(http.StatusServiceUnavailable, gin.H{
			"err": fmt.Sprint(err),
		})
		return
	}

	binding.JSON.Bind(c.Request, &alerts)

	s, err := json.Marshal(alerts)
	if err != nil {
		log.Print(err)
		return
	}

	log.Println("+------------------  A L E R T  J S O N  -------------------+")
	log.Printf("%s", s)
	log.Println("+-----------------------------------------------------------+\n\n")

	// Decide how format Text
	if cfg.TemplatePath == "" {
		msgtext = AlertFormatStandard(alerts)
	} else {
		msgtext = AlertFormatTemplate(alerts)
	}
	// Print in Log result message
	log.Println("+---------------  F I N A L   M E S S A G E  ---------------+")
	log.Println(msgtext)
	log.Println("+-----------------------------------------------------------+")

	msg := tgbotapi.NewMessage(chatid, msgtext)
	msg.ParseMode = tgbotapi.ModeHTML

	msg.DisableWebPagePreview = true

	sendmsg, err := bot.Send(msg)
	if err == nil {
		c.String(http.StatusOK, "telegram msg sent.")
	} else {
		log.Printf("Error sending message: %s", err)
		c.JSON(http.StatusServiceUnavailable, gin.H{
			"err":     fmt.Sprint(err),
			"message": sendmsg,
			"srcmsg":  fmt.Sprint(msgtext),
		})
		msg := tgbotapi.NewMessage(chatid, "Error sending message, checkout logs")
		bot.Send(msg)
	}
}
<|endoftext|>


<commit_before>// A minecraft server manager and map generator
package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/rpc"
	"net/rpc/jsonrpc"
	"os"
	"os/signal"

	"golang.org/x/net/websocket"
)

var (
	config     *Config
	controller *Controller
)

func main() {
	configFile := flag.String("-c", "config.json", "config file")
	flag.Parse()

	var err error
	config, err = loadConfig(*configFile)
	if err != nil {
		fmt.Println(err)
		return
	}

	controller = &Controller{
		config,
		make(map[int]*running),
	}
	err = rpc.RegisterName("RPC", RPC{config})
	if err != nil {
		fmt.Println(err)
		return
	}

	http.Handle("/upload", websocket.Handler(uploadHandler))
	http.Handle("/rpc", websocket.Handler(func(conn *websocket.Conn) { jsonrpc.ServeConn(conn) }))
	http.Handle("/", http.FileServer(hdir))
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", config.Port))
	if err != nil {
		log.Println(err)
		return
	}

	c := make(chan os.Signal, 1)
	go func() {
		defer l.Close()
		log.Println("Server Started")
		signal.Notify(c, os.Interrupt)
		defer signal.Stop(c)
		<-c
		close(c)
		log.Println("Closing")
	}()

	err = http.Serve(l, nil)
	select {
	case <-c:
	default:
		close(c)
		log.Println(err)
	}
}
<commit_msg>Corrected flag<commit_after>// A minecraft server manager and map generator
package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/rpc"
	"net/rpc/jsonrpc"
	"os"
	"os/signal"

	"golang.org/x/net/websocket"
)

var (
	config     *Config
	controller *Controller
)

func main() {
	configFile := flag.String("c", "config.json", "config file")
	flag.Parse()

	var err error
	config, err = loadConfig(*configFile)
	if err != nil {
		fmt.Println(err)
		return
	}

	controller = &Controller{
		config,
		make(map[int]*running),
	}
	err = rpc.RegisterName("RPC", RPC{config})
	if err != nil {
		fmt.Println(err)
		return
	}

	http.Handle("/upload", websocket.Handler(uploadHandler))
	http.Handle("/rpc", websocket.Handler(func(conn *websocket.Conn) { jsonrpc.ServeConn(conn) }))
	http.Handle("/", http.FileServer(dir))
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", config.Port))
	if err != nil {
		log.Println(err)
		return
	}

	c := make(chan os.Signal, 1)
	go func() {
		defer l.Close()
		log.Println("Server Started")
		signal.Notify(c, os.Interrupt)
		defer signal.Stop(c)
		<-c
		close(c)
		log.Println("Closing")
	}()

	err = http.Serve(l, nil)
	select {
	case <-c:
	default:
		close(c)
		log.Println(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

const (
	Black  = `0`
	Red    = `1`
	Green  = `2`
	Brown  = `3`
	Blue   = `4`
	Purple = `5`
	Cyan   = `6`
	Gray   = `7`
)

// Returns the ANSI colour code for the given background and foreground
// Note Bold is usually interpeted as 'light' these days. E.g. 'light blue.'
func Color(bg string, fg string, bold bool) string {
	var boldstr string
	if bold {
		boldstr = `1`
	} else {
		boldstr = `0`
	}
	return "\033[4" + bg + `;` + boldstr + `;3` + fg + `m`

}

func InverseColor() string {
	return "\033[7m"
}

func ResetColor() string {
	return "\033[0m"
}

// TerminalSize returns the width and height of the tty, respectively
func TerminalSize() (int, int, error) {
	cmd := exec.Command("stty", "size")
	cmd.Stdin = os.Stdin
	out, err := cmd.Output()
	if err != nil {
		return 0, 0, err
	}
	outarr := strings.Split(string(out), " ")
	if len(outarr) != 2 {
		return 0, 0, errors.New("Terminal size split error, stty size return unexpected: " + string(out))
	}

	height, err := strconv.Atoi(outarr[0])
	if err != nil {
		return 0, 0, errors.New("Termianl size height error, stty size return unexpected: " + string(outarr[0]))
	}

	width, err := strconv.Atoi(strings.Trim(outarr[1], " \n"))
	if err != nil {
		return 0, 0, errors.New("Terminal size width error, stty size return unexpected: " + string(outarr[1]))
	}

	return width, height, nil
}

type JsonHeading struct {
	Title    string   `json:"title"`
	Commands []string `json:"commands"`
}

type JsonHeadings struct {
	Headings []JsonHeading `json:"headings"`
}

func (h JsonHeading) Width() int {
	max := len(h.Title)
	for _, v := range h.Commands {
		if len(v) > max {
			max = len(v)
		}
	}
	return max
}

func (hs JsonHeadings) HeadingWidth() int {
	max := 0
	for _, v := range hs.Headings {
		if v.Width() > max {
			max = v.Width()
		}
	}
	return max
}

func (hs JsonHeadings) CommandsHeight() int {
	max := 0
	for _, v := range hs.Headings {
		if len(v.Commands) > max {
			max = len(v.Commands)
		}
	}
	return max
}

func (hs JsonHeadings) RowHasCommands(start, end, row int) bool {
	for i := start; i < end; i++ {
		heading := hs.Headings[i]
		if len(heading.Commands) >= (row + 1) {
			return true
		}
	}
	return false
}

func (hs *JsonHeadings) PrintHeadings(start, end, width int) string {
	if end > len(hs.Headings) {
		end = len(hs.Headings)
	}

	var s string
	for i := start; i != end; i++ {
		heading := hs.Headings[i]
		s += InverseColor()
		s += heading.Title + strings.Repeat(" ", width-len(heading.Title)-1)
		s += ResetColor()
		s += ` `
	}

	commandsHeight := hs.CommandsHeight()
	for j := 0; j != commandsHeight; j++ {
		if !hs.RowHasCommands(start, end, j) {
			continue
		}
		s += "\n"
		for i := start; i < end; i++ {
			heading := hs.Headings[i]
			if len(heading.Commands) <= j {
				s += strings.Repeat(" ", width)
				continue
			}
			command := heading.Commands[j]
			s += command + strings.Repeat(" ", width-len(command))
		}
	}

	return s
}

func (hs *JsonHeadings) PrintString(width int) string {
	var s string
	//	s += Color(Blue, Green, true) // debug

	headingWidth := hs.HeadingWidth() + 1
	headingsPerLine := width / headingWidth // +1 because headings are separated
	for i := 0; i < len(hs.Headings); i += headingsPerLine {
		s += hs.PrintHeadings(i, i+headingsPerLine, headingWidth) + "\n"
	}
	return s
}

func main() {
	if len(os.Args) < 2 {
		fmt.Println(`Usage: cman <program>`)
		return
	}

	filename := os.Args[1] + `.json`

	if _, err := os.Stat(filename); os.IsNotExist(err) {
		fmt.Println(`Cheatsheet Man does not exist for ` + os.Args[1])
		return
	}

	file, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Printf("File error: %v\n", err)
		return
	}

	var jsonHeadings JsonHeadings
	err = json.Unmarshal(file, &jsonHeadings)
	if err != nil {
		fmt.Printf("JSON unmarshal error: %v\n", err)
		return
	}

	//	fmt.Println(jsonHeadings)

	width, _, err := TerminalSize()
	if err != nil {
		fmt.Printf("Terminal size error: %v\n", err)
		return
	}

	//	fmt.Printf("Terminal Size: %vx%v\n", width, height)

	fmt.Println(jsonHeadings.PrintString(width))
}
<commit_msg>Removed debug code<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

const (
	Black  = `0`
	Red    = `1`
	Green  = `2`
	Brown  = `3`
	Blue   = `4`
	Purple = `5`
	Cyan   = `6`
	Gray   = `7`
)

// Color returns the ANSI colour code for the given background and foreground
// Note Bold is usually interpeted as 'light' these days. E.g. 'light blue.'
func Color(bg string, fg string, bold bool) string {
	var boldstr string
	if bold {
		boldstr = `1`
	} else {
		boldstr = `0`
	}
	return "\033[4" + bg + `;` + boldstr + `;3` + fg + `m`

}

func InverseColor() string {
	return "\033[7m"
}

func ResetColor() string {
	return "\033[0m"
}

// TerminalSize returns the width and height of the tty, respectively
func TerminalSize() (int, int, error) {
	cmd := exec.Command("stty", "size")
	cmd.Stdin = os.Stdin
	out, err := cmd.Output()
	if err != nil {
		return 0, 0, err
	}
	outarr := strings.Split(string(out), " ")
	if len(outarr) != 2 {
		return 0, 0, errors.New("Terminal size split error, stty size return unexpected: " + string(out))
	}

	height, err := strconv.Atoi(outarr[0])
	if err != nil {
		return 0, 0, errors.New("Termianl size height error, stty size return unexpected: " + string(outarr[0]))
	}

	width, err := strconv.Atoi(strings.Trim(outarr[1], " \n"))
	if err != nil {
		return 0, 0, errors.New("Terminal size width error, stty size return unexpected: " + string(outarr[1]))
	}

	return width, height, nil
}

type JsonHeading struct {
	Title    string   `json:"title"`
	Commands []string `json:"commands"`
}

type JsonHeadings struct {
	Headings []JsonHeading `json:"headings"`
}

func (h JsonHeading) Width() int {
	max := len(h.Title)
	for _, v := range h.Commands {
		if len(v) > max {
			max = len(v)
		}
	}
	return max
}

func (hs JsonHeadings) HeadingWidth() int {
	max := 0
	for _, v := range hs.Headings {
		if v.Width() > max {
			max = v.Width()
		}
	}
	return max
}

func (hs JsonHeadings) CommandsHeight() int {
	max := 0
	for _, v := range hs.Headings {
		if len(v.Commands) > max {
			max = len(v.Commands)
		}
	}
	return max
}

func (hs JsonHeadings) RowHasCommands(start, end, row int) bool {
	for i := start; i < end; i++ {
		heading := hs.Headings[i]
		if len(heading.Commands) >= (row + 1) {
			return true
		}
	}
	return false
}

func (hs *JsonHeadings) PrintHeadings(start, end, width int) string {
	if end > len(hs.Headings) {
		end = len(hs.Headings)
	}

	var s string
	for i := start; i != end; i++ {
		heading := hs.Headings[i]
		s += InverseColor()
		s += heading.Title + strings.Repeat(" ", width-len(heading.Title)-1)
		s += ResetColor()
		s += ` `
	}

	commandsHeight := hs.CommandsHeight()
	for j := 0; j != commandsHeight; j++ {
		if !hs.RowHasCommands(start, end, j) {
			continue
		}
		s += "\n"
		for i := start; i < end; i++ {
			heading := hs.Headings[i]
			if len(heading.Commands) <= j {
				s += strings.Repeat(" ", width)
				continue
			}
			command := heading.Commands[j]
			s += command + strings.Repeat(" ", width-len(command))
		}
	}

	return s
}

func (hs *JsonHeadings) PrintString(width int) string {
	var s string

	headingWidth := hs.HeadingWidth() + 1
	headingsPerLine := width / headingWidth // +1 because headings are separated
	for i := 0; i < len(hs.Headings); i += headingsPerLine {
		s += hs.PrintHeadings(i, i+headingsPerLine, headingWidth) + "\n"
	}
	return s
}

func main() {
	if len(os.Args) < 2 {
		fmt.Println(`Usage: cman <program>`)
		return
	}

	filename := os.Args[1] + `.json`

	if _, err := os.Stat(filename); os.IsNotExist(err) {
		fmt.Println(`Cheatsheet Man does not exist for ` + os.Args[1])
		return
	}

	file, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Printf("File error: %v\n", err)
		return
	}

	var jsonHeadings JsonHeadings
	err = json.Unmarshal(file, &jsonHeadings)
	if err != nil {
		fmt.Printf("JSON unmarshal error: %v\n", err)
		return
	}

	width, _, err := TerminalSize()
	if err != nil {
		fmt.Printf("Terminal size error: %v\n", err)
		return
	}

	fmt.Println(jsonHeadings.PrintString(width))
}
<|endoftext|>


<commit_before>package websocket

import (
	"bytes"
	"errors"
	"io"
	"net/url"
	"runtime"
	"time"

	"github.com/gopherjs/gopherjs/js"
	"honnef.co/go/js/dom"
	"honnef.co/go/js/util"
)

type ReadyState uint16

const (
	// Ready state constants from
	// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Ready_state_constants
	Connecting ReadyState = 0 // The connection is not yet open.
	Open                  = 1 // The connection is open and ready to communicate.
	Closing               = 2 // The connection is in the process of closing.
	Closed                = 3 // The connection is closed or couldn't be opened.
)

var (
	ErrSocketClosed = errors.New("the socket has been closed")
)

type Addr struct {
	*url.URL
}

// Network returns the network type for a WebSocket, "websocket".
func (addr *Addr) Network() string { return "websocket" }

type receiveItem struct {
	Error error
	Event *dom.MessageEvent
}

type WebSocket struct {
	js.Object
	util.EventTarget

	// See https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Attributes
	// for information about these attributes.
	BinaryType     string     `js:"binaryType"`
	BufferedAmount uint32     `js:"bufferedAmount"`
	Extensions     string     `js:"extensions"`
	Protocol       string     `js:"protocol"`
	ReadyState     ReadyState `js:"readyState"`
	URL            string     `js:"url"`

	openCh chan *js.Error

	ch      chan *receiveItem
	readBuf *bytes.Reader
}

// New creates a new WebSocket. It blocks until the connection opens or throws
// an error.
func New(url string) (*WebSocket, error) {
	object := js.Global.Get("WebSocket").New(url)

	ws := &WebSocket{
		Object:      object,
		EventTarget: util.EventTarget{Object: object},
		ch:          make(chan *receiveItem),
		openCh:      make(chan *js.Error),
	}
	ws.init()

	// Wait for the WebSocket to open or error. See: onOpen & onClose.
	err, ok := <-ws.openCh
	if ok && err != nil {
		ws.Close() // Just in case the connection was open for some reason?
		return nil, err
	}

	return ws, nil
}

func (ws *WebSocket) init() {
	// Some browsers don't support Blobs. On top of that, []byte is converted to
	// Int8Array, which is handled similarly to ArrayBuffer.
	ws.BinaryType = "arraybuffer"

	// Add all of the event handlers.
	ws.EventTarget.AddEventListener("open", false, ws.onOpen)
	ws.EventTarget.AddEventListener("close", false, ws.onClose)
	ws.EventTarget.AddEventListener("error", false, ws.onError)
	ws.EventTarget.AddEventListener("message", false, ws.onMessage)
}

func (ws *WebSocket) onOpen(event js.Object) {
	close(ws.openCh)
}

func (ws *WebSocket) onClose(event js.Object) {
	if wasClean := event.Get("wasClean").Bool(); !wasClean {
		go func() {
			defer func() {
				// This feels extremely hacky, but I can't think of a better way
				// to do it. openCh is closed before the end of New(), but this
				// is one of the paths that can close it. The other is in
				// WebSocket.onOpen.
				e := recover()
				if e == nil {
					return
				}
				if e, ok := e.(runtime.Error); ok && e.Error() == "runtime error: send on closed channel" {
					return
				}
				panic(e)
			}()

			// If the close wasn't clean, we need to inform the openCh. This
			// allows New to return an error.
			ws.openCh <- &js.Error{Object: event}
			close(ws.openCh)
		}()
	}
	close(ws.ch)
}

func (ws *WebSocket) onError(event js.Object) {
	// TODO: Don't send to ws.ch when this is a connection error.
	// onError is called when a connection fails. Such errors shouldn't be sent
	// to ws.ch.
	go func() {
		// This allows Receive to return an error. It seems that many
		// WebSocket.send errors are handled this way.
		ws.ch <- &receiveItem{
			Event: nil,
			Error: &js.Error{Object: event},
		}
	}()
}

func (ws *WebSocket) onMessage(event js.Object) {
	go func() {
		ws.ch <- &receiveItem{
			Event: dom.WrapEvent(event).(*dom.MessageEvent),
			Error: nil,
		}
	}()
}

// sendRaw sends a message on the WebSocket. The data argument can be a string
// or a js.Object containing an ArrayBuffer.
//
// The helper functions WriteString and SendBinary should be preferred to this.
func (ws *WebSocket) sendRaw(data interface{}) (err error) {
	defer func() {
		e := recover()
		if e == nil {
			return
		}
		if jsErr, ok := e.(*js.Error); ok && jsErr != nil {
			err = jsErr
		} else {
			panic(e)
		}
	}()
	ws.Object.Call("send", data)
	return nil
}

// ReceiveFrame receives one full frame from the WebSocket. It blocks until the
// frame is received.
func (ws *WebSocket) ReceiveFrame() (*dom.MessageEvent, error) {
	item, ok := <-ws.ch
	if !ok { // The channel has been closed
		return nil, ErrSocketClosed
	}
	return item.Event, item.Error
}

// SendString sends a string on the WebSocket. This is a helper method that
// calls SendRaw.
func (ws *WebSocket) WriteString(data string) (n int, err error) {
	err = ws.sendRaw(data)
	if err != nil {
		n = len(data)
	}
	return
}

// Read implements the io.Reader interface:
// It reads the data of a frame from the WebSocket connection. If b is not large
// enough, the next Read will read the rest of that frame.
func (ws *WebSocket) Read(b []byte) (n int, err error) {
	if ws.readBuf != nil {
		n, err = ws.readBuf.Read(b)
		if err == io.EOF {
			ws.readBuf = nil
			err = nil
		}
		// If we read nothing from the buffer, continue to trying to receive.
		// This saves us when the last Read call emptied the buffer and this
		// call triggers the EOF. There's probably a better way of doing this,
		// but I'm really tired.
		if n > 0 {
			return
		}
	}

	frame, err := ws.ReceiveFrame()
	if err != nil {
		return 0, err
	}

	receivedBytes := []byte(frame.Data.Str())
	// fast path
	if len(b) >= len(receivedBytes) {
		n = copy(b, receivedBytes)
		return
	}

	ws.readBuf = bytes.NewReader(receivedBytes)

	n, err = ws.readBuf.Read(b)
	if err == io.EOF {
		ws.readBuf = nil
		err = nil
	}
	return
}

// Write sends binary data on the WebSocket.
//
// Note: There are cases where the browser will throw an exception if it
// believes that the data passed to this function may be UTF-8.
func (ws *WebSocket) Write(p []byte) (n int, err error) {
	// We use Write to conform with the io.Writer interface.
	err = ws.sendRaw(p)
	if err != nil {
		n = len(p)
	}
	return
}

// Close closes the underlying WebSocket and cleans up any resources associated
// with the helper.
func (ws *WebSocket) Close() (err error) {
	defer func() {
		e := recover()
		if e == nil {
			return
		}
		if jsErr, ok := e.(*js.Error); ok && jsErr != nil {
			err = jsErr
		} else {
			panic(e)
		}
	}()
	ws.Object.Call("close")
	return nil
}

func (ws *WebSocket) LocalAddr() *Addr {
	// We can't use net.Addr, because net.init() causes a panic due to attempts
	// to make syscalls.

	// TODO: Find a more graceful way to handle this.
	panic("It is not possible to implement this function due to limitations within JavaScript")
}

func (ws *WebSocket) RemoteAddr() *Addr {
	// We can't use net.Addr, because net.init() causes a panic due to attempts
	// to make syscalls.

	wsURL, err := url.Parse(ws.URL)
	if err != nil {
		// TODO: Should we be panicking for this?
		panic(err)
	}
	return &Addr{wsURL}
}

func (ws *WebSocket) SetDeadline(t time.Time) error {
	// TODO: Implement
	panic("not yet implemeneted")
}

func (ws *WebSocket) SetReadDeadline(t time.Time) error {
	// TODO: Implement
	panic("not yet implemeneted")
}

func (ws *WebSocket) SetWriteDeadline(t time.Time) error {
	// TODO: Implement
	panic("not yet implemeneted")
}
<commit_msg>Add a note<commit_after>package websocket

import (
	"bytes"
	"errors"
	"io"
	"net/url"
	"runtime"
	"time"

	"github.com/gopherjs/gopherjs/js"
	"honnef.co/go/js/dom"
	"honnef.co/go/js/util"
)

type ReadyState uint16

const (
	// Ready state constants from
	// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Ready_state_constants
	Connecting ReadyState = 0 // The connection is not yet open.
	Open                  = 1 // The connection is open and ready to communicate.
	Closing               = 2 // The connection is in the process of closing.
	Closed                = 3 // The connection is closed or couldn't be opened.
)

var (
	ErrSocketClosed = errors.New("the socket has been closed")
)

type Addr struct {
	*url.URL
}

// Network returns the network type for a WebSocket, "websocket".
func (addr *Addr) Network() string { return "websocket" }

type receiveItem struct {
	Error error
	Event *dom.MessageEvent
}

type WebSocket struct {
	js.Object
	util.EventTarget

	// See https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Attributes
	// for information about these attributes.
	BinaryType     string     `js:"binaryType"`
	BufferedAmount uint32     `js:"bufferedAmount"`
	Extensions     string     `js:"extensions"`
	Protocol       string     `js:"protocol"`
	ReadyState     ReadyState `js:"readyState"`
	URL            string     `js:"url"`

	openCh chan *js.Error

	ch      chan *receiveItem
	readBuf *bytes.Reader
}

// New creates a new WebSocket. It blocks until the connection opens or throws
// an error.
func New(url string) (*WebSocket, error) {
	object := js.Global.Get("WebSocket").New(url)

	ws := &WebSocket{
		Object:      object,
		EventTarget: util.EventTarget{Object: object},
		ch:          make(chan *receiveItem),
		openCh:      make(chan *js.Error),
	}
	ws.init()

	// Wait for the WebSocket to open or error. See: onOpen & onClose.
	err, ok := <-ws.openCh
	if ok && err != nil {
		ws.Close() // Just in case the connection was open for some reason?
		return nil, err
	}

	return ws, nil
}

func (ws *WebSocket) init() {
	// Some browsers don't support Blobs. On top of that, []byte is converted to
	// Int8Array, which is handled similarly to ArrayBuffer.
	ws.BinaryType = "arraybuffer"

	// Add all of the event handlers.
	ws.EventTarget.AddEventListener("open", false, ws.onOpen)
	ws.EventTarget.AddEventListener("close", false, ws.onClose)
	ws.EventTarget.AddEventListener("error", false, ws.onError)
	ws.EventTarget.AddEventListener("message", false, ws.onMessage)
}

func (ws *WebSocket) onOpen(event js.Object) {
	close(ws.openCh)
}

func (ws *WebSocket) onClose(event js.Object) {
	if wasClean := event.Get("wasClean").Bool(); !wasClean {
		go func() {
			defer func() {
				// This feels extremely hacky, but I can't think of a better way
				// to do it. openCh is closed before the end of New(), but this
				// is one of the paths that can close it. The other is in
				// WebSocket.onOpen.
				e := recover()
				if e == nil {
					return
				}
				if e, ok := e.(runtime.Error); ok && e.Error() == "runtime error: send on closed channel" {
					return
				}
				panic(e)
			}()

			// If the close wasn't clean, we need to inform the openCh. This
			// allows New to return an error.
			ws.openCh <- &js.Error{Object: event}
			close(ws.openCh)
		}()
	}
	close(ws.ch)
}

func (ws *WebSocket) onError(event js.Object) {
	// TODO: Don't send to ws.ch when this is a connection error.
	// onError is called when a connection fails. Such errors shouldn't be sent
	// to ws.ch.
	go func() {
		// This allows Receive to return an error. It seems that many
		// WebSocket.send errors are handled this way.
		ws.ch <- &receiveItem{
			Event: nil,
			Error: &js.Error{Object: event},
		}
	}()
}

func (ws *WebSocket) onMessage(event js.Object) {
	go func() {
		ws.ch <- &receiveItem{
			Event: dom.WrapEvent(event).(*dom.MessageEvent),
			Error: nil,
		}
	}()
}

// sendRaw sends a message on the WebSocket. The data argument can be a string
// or a js.Object containing an ArrayBuffer.
//
// The helper functions WriteString and SendBinary should be preferred to this.
func (ws *WebSocket) sendRaw(data interface{}) (err error) {
	defer func() {
		e := recover()
		if e == nil {
			return
		}
		if jsErr, ok := e.(*js.Error); ok && jsErr != nil {
			err = jsErr
		} else {
			panic(e)
		}
	}()
	ws.Object.Call("send", data)
	return nil
}

// ReceiveFrame receives one full frame from the WebSocket. It blocks until the
// frame is received.
func (ws *WebSocket) ReceiveFrame() (*dom.MessageEvent, error) {
	item, ok := <-ws.ch
	if !ok { // The channel has been closed
		return nil, ErrSocketClosed
	}
	return item.Event, item.Error
}

// SendString sends a string on the WebSocket. This is a helper method that
// calls SendRaw.
func (ws *WebSocket) WriteString(data string) (n int, err error) {
	err = ws.sendRaw(data)
	if err != nil {
		n = len(data)
	}
	return
}

// Read implements the io.Reader interface:
// It reads the data of a frame from the WebSocket connection. If b is not large
// enough, the next Read will read the rest of that frame.
func (ws *WebSocket) Read(b []byte) (n int, err error) {
	// TODO: Make this fully full the buffer once the Timeout functions are
	// implemented.

	if ws.readBuf != nil {
		n, err = ws.readBuf.Read(b)
		if err == io.EOF {
			ws.readBuf = nil
			err = nil
		}
		// If we read nothing from the buffer, continue to trying to receive.
		// This saves us when the last Read call emptied the buffer and this
		// call triggers the EOF. There's probably a better way of doing this,
		// but I'm really tired.
		if n > 0 {
			return
		}
	}

	frame, err := ws.ReceiveFrame()
	if err != nil {
		return 0, err
	}

	receivedBytes := []byte(frame.Data.Str())
	// fast path
	if len(b) >= len(receivedBytes) {
		n = copy(b, receivedBytes)
		return
	}

	ws.readBuf = bytes.NewReader(receivedBytes)

	n, err = ws.readBuf.Read(b)
	if err == io.EOF {
		ws.readBuf = nil
		err = nil
	}
	return
}

// Write sends binary data on the WebSocket.
//
// Note: There are cases where the browser will throw an exception if it
// believes that the data passed to this function may be UTF-8.
func (ws *WebSocket) Write(p []byte) (n int, err error) {
	// We use Write to conform with the io.Writer interface.
	err = ws.sendRaw(p)
	if err != nil {
		n = len(p)
	}
	return
}

// Close closes the underlying WebSocket and cleans up any resources associated
// with the helper.
func (ws *WebSocket) Close() (err error) {
	defer func() {
		e := recover()
		if e == nil {
			return
		}
		if jsErr, ok := e.(*js.Error); ok && jsErr != nil {
			err = jsErr
		} else {
			panic(e)
		}
	}()
	ws.Object.Call("close")
	return nil
}

func (ws *WebSocket) LocalAddr() *Addr {
	// We can't use net.Addr, because net.init() causes a panic due to attempts
	// to make syscalls.

	// TODO: Find a more graceful way to handle this.
	panic("It is not possible to implement this function due to limitations within JavaScript")
}

func (ws *WebSocket) RemoteAddr() *Addr {
	// We can't use net.Addr, because net.init() causes a panic due to attempts
	// to make syscalls.

	wsURL, err := url.Parse(ws.URL)
	if err != nil {
		// TODO: Should we be panicking for this?
		panic(err)
	}
	return &Addr{wsURL}
}

func (ws *WebSocket) SetDeadline(t time.Time) error {
	// TODO: Implement
	panic("not yet implemeneted")
}

func (ws *WebSocket) SetReadDeadline(t time.Time) error {
	// TODO: Implement
	panic("not yet implemeneted")
}

func (ws *WebSocket) SetWriteDeadline(t time.Time) error {
	// TODO: Implement
	panic("not yet implemeneted")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"path"
	"strconv"
	"strings"
)

func panicWrapper(err error) {
	if err != nil {
		panic(err)
	}
}

func initConfig() (string, string, string) {

	target := flag.String("target", "http://habrahabr.ru/", "proxy target")
	search := flag.String("search", "", "Seeking sentence")
	replace := flag.String("replace", "", "Replace sentence")

	flag.Parse()

	return *target, *search, *replace
}

func getRequestString(targetLink string) string {

	resp, err := http.Get(targetLink)
	panicWrapper(err)
	defer resp.Body.Close()
	bodyData, err := ioutil.ReadAll(resp.Body)
	panicWrapper(err)

	result := string(bodyData)

	return result
}

func main() {
	var serverPort int = 3001
	var serverHost string = ""
	var listenAddress string = serverHost + ":" + strconv.Itoa(serverPort)

	target, search, replace := initConfig()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

		outsidePath := "http://" + path.Clean(strings.Replace(target, "http://", "", 1)+r.URL.Path)

		result := getRequestString(outsidePath)
		result = strings.Replace(result, search, replace, -1)

		fmt.Fprint(w, result)

	})

	fmt.Printf(`
        Server started %s
        Target host: %s
        Seeking sentence: %s
        Replace for next sentence: %s

    `, listenAddress, target, search, replace)

	err := http.ListenAndServe(listenAddress, nil)
	panicWrapper(err)
}
<commit_msg>Remarks<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"path"
	"strconv"
	"strings"
)

// Примечание: Названия у функций начинаются со строчной буквы  по причине того,
// что они используются только в текущем пакете и нет смысла делать их публичными

func panicWrapper(err error) {
	if err != nil {
		panic(err)
	}
}

func initConfig() (string, string, string) {

	target := flag.String("target", "http://habrahabr.ru/", "Proxy target")
	search := flag.String("search", "", "Seeking sentence")
	replace := flag.String("replace", "", "Replace sentence")

	flag.Parse()

	return *target, *search, *replace
}

func getRequestString(targetLink string) string {

	resp, err := http.Get(targetLink)
	panicWrapper(err)
	defer resp.Body.Close()
	bodyData, err := ioutil.ReadAll(resp.Body)
	panicWrapper(err)
	result := string(bodyData)
	return result
}

func main() {
	var serverPort int = 3001
	var serverHost string = ""
	var listenAddress string = serverHost + ":" + strconv.Itoa(serverPort)
	target, search, replace := initConfig()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

		outsidePath := "http://" + path.Clean(strings.Replace(target, "http://", "", 1)+r.URL.Path)
		result := getRequestString(outsidePath)
		result = strings.Replace(result, search, replace, -1)

		fmt.Fprint(w, result)
	})

	fmt.Printf(`
        Server started %s
        Target host: %s
        Seeking sentence: %s
        Replace for next sentence: %s

    `, listenAddress, target, search, replace)

	err := http.ListenAndServe(listenAddress, nil)
	panicWrapper(err)
}
<|endoftext|>


<commit_before>// Package staircase provides a step function with smooth transitions between
// steps based on cubic Hermite splines.
//
// https://en.wikipedia.org/wiki/Step_function
//
// https://en.wikipedia.org/wiki/Cubic_Hermite_spline
package staircase

import (
	"errors"
)

// Staircase represents a step function with smooth transitions.
type Staircase struct {
	lengths    []float64
	heights    []float64
	distances  []float64
	transition float64
}

// New returns a step function with smooth transitions.
func New(lengths, heights []float64, transition float64) (*Staircase, error) {
	if transition <= 0 || transition > 0.5 {
		return nil, errors.New("the transition length should be in (0, 0.5]")
	}

	n := len(lengths)

	distances := make([]float64, n)
	distances[0] = lengths[0]
	for i := 1; i < n; i++ {
		distances[i] = distances[i-1] + lengths[i]
	}

	staircase := &Staircase{
		lengths:    lengths,
		heights:    heights,
		distances:  distances,
		transition: transition,
	}

	return staircase, nil
}

// Evaluate computes the function at a point.
func (self *Staircase) Evaluate(point float64) float64 {
	l, d, h := self.lengths, self.distances, self.heights
	n := len(l)

	if point < 0 {
		return h[0]
	}
	if point > d[n-1] {
		return h[n-1]
	}

	k := 0
	for point > d[k] {
		k++
	}

	if k > 0 {
		point -= d[k-1]
	}

	if z, t := point/l[k], self.transition; k > 0 && z <= t {
		return hermite((t*l[k-1]+point)/(t*l[k-1]+t*l[k]), h[k-1], h[k])
	} else if k < n-1 && z >= 1-t {
		return hermite((point-(1-t)*l[k])/(t*l[k]+t*l[k+1]), h[k], h[k+1])
	}

	return h[k]
}

func hermite(t, a, b float64) float64 {
	t2 := t * t
	t3 := t * t2
	return (2*t3-3*t2+1)*a + (-2*t3+3*t2)*b
}
<commit_msg>A bit of refactoring<commit_after>// Package staircase provides a step function with smooth transitions between
// steps based on cubic Hermite splines.
//
// https://en.wikipedia.org/wiki/Step_function
//
// https://en.wikipedia.org/wiki/Cubic_Hermite_spline
package staircase

import (
	"errors"
)

// Staircase represents a step function with smooth transitions.
type Staircase struct {
	lengths    []float64
	heights    []float64
	transition float64
}

// New returns a step function with smooth transitions.
func New(lengths, heights []float64, transition float64) (*Staircase, error) {
	if transition <= 0 || transition > 0.5 {
		return nil, errors.New("the transition length should be in (0, 0.5]")
	}

	staircase := &Staircase{
		lengths:    lengths,
		heights:    heights,
		transition: transition,
	}

	return staircase, nil
}

// Evaluate computes the function at a point.
func (self *Staircase) Evaluate(point float64) float64 {
	h := self.heights

	if point < 0 {
		return h[0]
	}

	l := self.lengths
	n := len(l)

	k := 0
	for k < n && point > l[k] {
		point -= l[k]
		k++
	}

	if k == n {
		return h[n-1]
	}

	if z, t := point/l[k], self.transition; k > 0 && z <= t {
		return hermite((t*l[k-1]+point)/(t*l[k-1]+t*l[k]), h[k-1], h[k])
	} else if k < n-1 && z >= 1-t {
		return hermite((point-(1-t)*l[k])/(t*l[k]+t*l[k+1]), h[k], h[k+1])
	}

	return h[k]
}

func hermite(t, a, b float64) float64 {
	t2 := t * t
	t3 := t * t2
	return (2*t3-3*t2+1)*a + (-2*t3+3*t2)*b
}
<|endoftext|>


<commit_before>package main

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

	"github.com/dtan4/ct2stimer/crontab"
	"github.com/dtan4/ct2stimer/systemd"
	flag "github.com/spf13/pflag"
)

var opts = struct {
	after    string
	filename string
	outdir   string
	reload   bool
}{}

var defaultRegexp = regexp.MustCompile(`--name ([a-zA-Z0-9._-]+)`)

func parseArgs(args []string) error {
	f := flag.NewFlagSet("ct2stimer", flag.ExitOnError)

	f.StringVar(&opts.filename, "after", "", "unit dependencies (After=)")
	f.StringVarP(&opts.filename, "file", "f", "", "crontab file")
	f.StringVarP(&opts.outdir, "outdir", "o", systemd.DefaultUnitsDirectory, "directory to save systemd files")
	f.BoolVar(&opts.reload, "reload", false, "reload & start genreated timers")

	f.Parse(args)

	if opts.filename == "" {
		return fmt.Errorf("Please specify crontab file.")
	}

	if opts.outdir == "" {
		return fmt.Errorf("Please specify directory to save systemd files.")
	}

	if _, err := os.Stat(opts.outdir); err != nil {
		if os.IsNotExist(err) {
			return fmt.Errorf("%s: directory does not exist", opts.outdir)
		}

		return err
	}

	return nil
}

func reloadSystemd(timers []string) error {
	conn, err := systemd.NewConn()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	defer conn.Close()

	client := systemd.NewClient(conn)

	if err := client.Reload(); err != nil {
		return err
	}

	for _, timerUnit := range timers {
		if err := client.StartUnit(timerUnit); err != nil {
			return err
		}
	}

	return nil
}

func main() {
	if err := parseArgs(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	body, err := ioutil.ReadFile(opts.filename)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	schedules, err := crontab.Parse(string(body))
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	timers := []string{}

	for _, schedule := range schedules {
		calendar, err := schedule.ConvertToSystemdCalendar()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		name := schedule.NameByRegexp(defaultRegexp)
		if name == "" {
			name = "cron-" + schedule.SHA256Sum()[0:12]
		}

		service, err := systemd.GenerateService(name, schedule.Command, opts.after)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		servicePath := filepath.Join(opts.outdir, name+".service")
		if err := ioutil.WriteFile(servicePath, []byte(service), 0644); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		timer, err := systemd.GenerateTimer(name, calendar)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		timerPath := filepath.Join(opts.outdir, name+".timer")
		if err := ioutil.WriteFile(timerPath, []byte(timer), 0644); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		timers = append(timers, name+".timer")
	}

	if opts.reload {
		if err := reloadSystemd(timers); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}
}
<commit_msg>Specify name regexp by CLI option<commit_after>package main

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

	"github.com/dtan4/ct2stimer/crontab"
	"github.com/dtan4/ct2stimer/systemd"
	flag "github.com/spf13/pflag"
)

var opts = struct {
	after      string
	filename   string
	nameRegexp string
	outdir     string
	reload     bool
}{}

func parseArgs(args []string) error {
	f := flag.NewFlagSet("ct2stimer", flag.ExitOnError)

	f.StringVar(&opts.filename, "after", "", "unit dependencies (After=)")
	f.StringVarP(&opts.filename, "file", "f", "", "crontab file")
	f.StringVar(&opts.nameRegexp, "name-regexp", "", "regexp to extract scheduler name from crontab")
	f.StringVarP(&opts.outdir, "outdir", "o", systemd.DefaultUnitsDirectory, "directory to save systemd files")
	f.BoolVar(&opts.reload, "reload", false, "reload & start genreated timers")

	f.Parse(args)

	if opts.filename == "" {
		return fmt.Errorf("Please specify crontab file.")
	}

	if opts.outdir == "" {
		return fmt.Errorf("Please specify directory to save systemd files.")
	}

	if _, err := os.Stat(opts.outdir); err != nil {
		if os.IsNotExist(err) {
			return fmt.Errorf("%s: directory does not exist", opts.outdir)
		}

		return err
	}

	return nil
}

func reloadSystemd(timers []string) error {
	conn, err := systemd.NewConn()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	defer conn.Close()

	client := systemd.NewClient(conn)

	if err := client.Reload(); err != nil {
		return err
	}

	for _, timerUnit := range timers {
		if err := client.StartUnit(timerUnit); err != nil {
			return err
		}
	}

	return nil
}

func main() {
	if err := parseArgs(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	body, err := ioutil.ReadFile(opts.filename)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	schedules, err := crontab.Parse(string(body))
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	timers := []string{}

	for _, schedule := range schedules {
		calendar, err := schedule.ConvertToSystemdCalendar()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		var name string

		if opts.nameRegexp != "" {
			re, err := regexp.Compile(opts.nameRegexp)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}

			name = schedule.NameByRegexp(re)
		}

		if name == "" {
			name = "cron-" + schedule.SHA256Sum()[0:12]
		}

		service, err := systemd.GenerateService(name, schedule.Command, opts.after)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		servicePath := filepath.Join(opts.outdir, name+".service")
		if err := ioutil.WriteFile(servicePath, []byte(service), 0644); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		timer, err := systemd.GenerateTimer(name, calendar)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		timerPath := filepath.Join(opts.outdir, name+".timer")
		if err := ioutil.WriteFile(timerPath, []byte(timer), 0644); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		timers = append(timers, name+".timer")
	}

	if opts.reload {
		if err := reloadSystemd(timers); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}
}
<|endoftext|>