<commit_before>package main

import (
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/fzzbt/radix/redis"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"syscall"
	"time"
)

// HTTP headers struct
type Headers struct {
	contentType  string
	lastModified string
	cacheControl string
}

// The maximal size for an image is 5MB
const maxSize = 5 * (1 << 20)

// The directory for caching files
var directory string

// The connection to redis
var connection *redis.Client

func urlStatus(uri string) error {
	ok, err := connection.Hexists("img/"+uri, "created_at").Bool()
	if err != nil {
		return err
	}
	if !ok {
		return errors.New("Invalid URL")
	}

	str, err := connection.Get("img/err/" + uri).Str()
	if err == nil {
		return errors.New(str)
	}

	return nil
}

// Generate a key for cache from a string
func generateKeyForCache(s string) string {
	h := sha1.New()
	io.WriteString(h, s)
	key := h.Sum(nil)

	// Use 3 levels of hasing to avoid having too many files in the same directory
	return fmt.Sprintf("%s/%x/%x/%x/%x", directory, key[0:1], key[1:2], key[2:3], key[3:])
}

// Fetch image from cache
func fetchImageFromCache(uri string) (headers Headers, body []byte, ok bool) {
	ok = false

	contentType, err := connection.Hget("img/"+uri, "type").Str()
	if err != nil {
		return
	}

	filename := generateKeyForCache(uri)
	stat, err := os.Stat(filename)
	if err != nil {
		return
	}

	headers.contentType = contentType
	headers.lastModified = stat.ModTime().Format(time.RFC1123)

	body, err = ioutil.ReadFile(filename)
	ok = err == nil

	present, err := connection.Exists("img/updated/" + uri).Bool()
	if err == nil && !present {
		go fetchImageFromServer(uri)
	}

	return
}

// Save the body and the content-type header in cache
func saveImageInCache(uri string, headers Headers, body []byte) {
	go func() {
		filename := generateKeyForCache(uri)
		dirname := path.Dir(filename)
		err := os.MkdirAll(dirname, 0755)
		if err != nil {
			return
		}

		// Save the body on disk
		err = ioutil.WriteFile(filename, body, 0644)
		if err != nil {
			log.Printf("Error while writing %s\n", filename)
			return
		}

		// And other infos in redis
		connection.Hset("img/"+uri, "type", headers.contentType)
		connection.Set("img/updated/"+uri, headers.lastModified)
		connection.Expire("img/updated/"+uri, 600)
	}()
}

func saveErrorInCache(uri string, err error) {
	go func() {
		connection.Set("img/err/"+uri, err.Error())
		connection.Expire("img/err/"+uri, 600)
	}()
}

// Fetch the image from the distant server
func fetchImageFromServer(uri string) (headers Headers, body []byte, err error) {
	res, err := http.Get(uri)
	if err != nil {
		return
	}
	if res.StatusCode != 200 {
		log.Printf("Status code of %s is: %d\n", uri, res.StatusCode)
		err = errors.New("Unexpected status code")
		saveErrorInCache(uri, err)
		return
	}

	defer res.Body.Close()
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return
	}
	if res.ContentLength > maxSize {
		log.Printf("Exceeded max size for %s: %d\n", uri, res.ContentLength)
		err = errors.New("Exceeded max size")
		saveErrorInCache(uri, err)
		return
	}
	contentType := res.Header.Get("Content-Type")
	if contentType[0:5] != "image" {
		log.Printf("%s has an invalid content-type: %s\n", uri, contentType)
		err = errors.New("Invalid content-type")
		saveErrorInCache(uri, err)
		return
	}
	log.Printf("Fetch %s (%s)\n", uri, contentType)

	headers.contentType = contentType
	headers.lastModified = time.Now().Format(time.RFC1123)
	if urlStatus(uri) == nil {
		saveImageInCache(uri, headers, body)
	}
	return
}

// Fetch image from cache if available, or from the server
func fetchImage(uri string) (headers Headers, body []byte, err error) {
	err = urlStatus(uri)
	if err != nil {
		return
	}

	headers, body, ok := fetchImageFromCache(uri)
	if !ok {
		headers, body, err = fetchImageFromServer(uri)
	}
	headers.cacheControl = "public, max-age=600"

	return
}

// Receive an HTTP request for an image and respond with it
func Img(w http.ResponseWriter, r *http.Request) {
	encoded_url := r.URL.Query().Get(":encoded_url")
	chars, err := hex.DecodeString(encoded_url)
	if err != nil {
		log.Printf("Invalid URL %s\n", encoded_url)
		http.Error(w, "Invalid parameters", 400)
		return
	}
	uri := string(chars)

	headers, body, err := fetchImage(uri)
	if err != nil {
		http.NotFound(w, r)
		return
	}
	if headers.lastModified == r.Header.Get("If-Modified-Since") {
		w.WriteHeader(http.StatusNotModified)
		return
	}
	w.Header().Add("Content-Type", headers.contentType)
	w.Header().Add("Last-Modified", headers.lastModified)
	w.Header().Add("Cache-Control", headers.cacheControl)
	w.Write(body)
}

// Returns 200 OK if the server is running (for monitoring)
func Status(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "OK")
}

func main() {
	// Parse the command-line
	var addr string
	var logs string
	var conn string
	flag.StringVar(&addr, "a", "127.0.0.1:8000", "Bind to this address:port")
	flag.StringVar(&logs, "l", "-", "Use this file for logs")
	flag.StringVar(&conn, "r", "localhost:6379/0", "The redis database to use for caching meta")
	flag.StringVar(&directory, "d", "cache", "The directory for the caching files")
	flag.Parse()

	// Logging
	if logs != "-" {
		f, err := os.OpenFile(logs, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			log.Fatal("OpenFile: ", err)
		}
		syscall.Dup2(int(f.Fd()), int(os.Stdout.Fd()))
		syscall.Dup2(int(f.Fd()), int(os.Stderr.Fd()))
	}

	// Redis
	parts := strings.Split(conn, "/")
	host := parts[0]
	db := 0
	if len(parts) >= 2 {
		db, _ = strconv.Atoi(parts[1])
	}
	cfg := redis.Config{Database: db, Address: host, PoolCapacity: 4}
	connection = redis.NewClient(cfg)
	defer connection.Close()

	// Routing
	m := pat.New()
	m.Get("/status", http.HandlerFunc(Status))
	m.Get("/img/:encoded_url/:filename", http.HandlerFunc(Img))
	m.Get("/img/:encoded_url", http.HandlerFunc(Img))
	http.Handle("/", m)

	// Start the HTTP server
	log.Printf("Listening on http://%s/\n", addr)
	err := http.ListenAndServe(addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>Serve avatars too<commit_after>package main

import (
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/fzzbt/radix/redis"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"syscall"
	"time"
)

// HTTP headers struct
type Headers struct {
	contentType  string
	lastModified string
	cacheControl string
}

// The URL for the default avatar
const defaultAvatarUrl = "//linuxfr.org/images/default-avatar.png"

// The maximal size for an image is 5MB
const maxSize = 5 * (1 << 20)

// The directory for caching files
var directory string

// The connection to redis
var connection *redis.Client

// Check if an URL is valid and not temporary in error
func urlStatus(uri string) error {
	ok, err := connection.Hexists("img/"+uri, "created_at").Bool()
	if err != nil {
		return err
	}
	if !ok {
		return errors.New("Invalid URL")
	}

	str, err := connection.Get("img/err/" + uri).Str()
	if err == nil {
		return errors.New(str)
	}

	return nil
}

// Generate a key for cache from a string
func generateKeyForCache(s string) string {
	h := sha1.New()
	io.WriteString(h, s)
	key := h.Sum(nil)

	// Use 3 levels of hasing to avoid having too many files in the same directory
	return fmt.Sprintf("%s/%x/%x/%x/%x", directory, key[0:1], key[1:2], key[2:3], key[3:])
}

// Fetch image from cache
func fetchImageFromCache(uri string) (headers Headers, body []byte, ok bool) {
	ok = false

	contentType, err := connection.Hget("img/"+uri, "type").Str()
	if err != nil {
		return
	}

	filename := generateKeyForCache(uri)
	stat, err := os.Stat(filename)
	if err != nil {
		return
	}

	headers.contentType = contentType
	headers.lastModified = stat.ModTime().Format(time.RFC1123)

	body, err = ioutil.ReadFile(filename)
	ok = err == nil

	present, err := connection.Exists("img/updated/" + uri).Bool()
	if err == nil && !present {
		go fetchImageFromServer(uri)
	}

	return
}

// Save the body and the content-type header in cache
func saveImageInCache(uri string, headers Headers, body []byte) {
	go func() {
		filename := generateKeyForCache(uri)
		dirname := path.Dir(filename)
		err := os.MkdirAll(dirname, 0755)
		if err != nil {
			return
		}

		// Save the body on disk
		err = ioutil.WriteFile(filename, body, 0644)
		if err != nil {
			log.Printf("Error while writing %s\n", filename)
			return
		}

		// And other infos in redis
		connection.Hset("img/"+uri, "type", headers.contentType)
		connection.Set("img/updated/"+uri, headers.lastModified)
		connection.Expire("img/updated/"+uri, 600)
	}()
}

// Save the error in redis for 10 minutes
func saveErrorInCache(uri string, err error) {
	go func() {
		connection.Set("img/err/"+uri, err.Error())
		connection.Expire("img/err/"+uri, 600)
	}()
}

// Fetch the image from the distant server
func fetchImageFromServer(uri string) (headers Headers, body []byte, err error) {
	res, err := http.Get(uri)
	if err != nil {
		return
	}
	if res.StatusCode != 200 {
		log.Printf("Status code of %s is: %d\n", uri, res.StatusCode)
		err = errors.New("Unexpected status code")
		saveErrorInCache(uri, err)
		return
	}

	defer res.Body.Close()
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return
	}
	if res.ContentLength > maxSize {
		log.Printf("Exceeded max size for %s: %d\n", uri, res.ContentLength)
		err = errors.New("Exceeded max size")
		saveErrorInCache(uri, err)
		return
	}
	contentType := res.Header.Get("Content-Type")
	if contentType[0:5] != "image" {
		log.Printf("%s has an invalid content-type: %s\n", uri, contentType)
		err = errors.New("Invalid content-type")
		saveErrorInCache(uri, err)
		return
	}
	log.Printf("Fetch %s (%s)\n", uri, contentType)

	headers.contentType = contentType
	headers.lastModified = time.Now().Format(time.RFC1123)
	if urlStatus(uri) == nil {
		saveImageInCache(uri, headers, body)
	}
	return
}

// Fetch image from cache if available, or from the server
func fetchImage(uri string) (headers Headers, body []byte, err error) {
	err = urlStatus(uri)
	if err != nil {
		return
	}

	headers, body, ok := fetchImageFromCache(uri)
	if !ok {
		headers, body, err = fetchImageFromServer(uri)
	}
	headers.cacheControl = "public, max-age=600"

	return
}

// Receive an HTTP request, fetch the image and respond with it
func Image(w http.ResponseWriter, r *http.Request, fn func()) {
	encoded_url := r.URL.Query().Get(":encoded_url")
	chars, err := hex.DecodeString(encoded_url)
	if err != nil {
		log.Printf("Invalid URL %s\n", encoded_url)
		http.Error(w, "Invalid parameters", 400)
		return
	}
	uri := string(chars)

	headers, body, err := fetchImage(uri)
	if err != nil {
		fn()
		return
	}
	if headers.lastModified == r.Header.Get("If-Modified-Since") {
		w.WriteHeader(http.StatusNotModified)
		return
	}
	w.Header().Add("Content-Type", headers.contentType)
	w.Header().Add("Last-Modified", headers.lastModified)
	w.Header().Add("Cache-Control", headers.cacheControl)
	w.Write(body)
}

// Receive an HTTP request for an image and respond with it
func Img(w http.ResponseWriter, r *http.Request) {
	fn := func() {
		http.NotFound(w, r)
	}
	Image(w, r, fn)
}

// Receive an HTTP request for an avatar and respond with it
func Avatar(w http.ResponseWriter, r *http.Request) {
	fn := func() {
		w.Header().Set("Location", defaultAvatarUrl)
		w.WriteHeader(http.StatusFound)
	}
	Image(w, r, fn)
}

// Returns 200 OK if the server is running (for monitoring)
func Status(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "OK")
}

func main() {
	// Parse the command-line
	var addr string
	var logs string
	var conn string
	flag.StringVar(&addr, "a", "127.0.0.1:8000", "Bind to this address:port")
	flag.StringVar(&logs, "l", "-", "Use this file for logs")
	flag.StringVar(&conn, "r", "localhost:6379/0", "The redis database to use for caching meta")
	flag.StringVar(&directory, "d", "cache", "The directory for the caching files")
	flag.Parse()

	// Logging
	if logs != "-" {
		f, err := os.OpenFile(logs, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			log.Fatal("OpenFile: ", err)
		}
		syscall.Dup2(int(f.Fd()), int(os.Stdout.Fd()))
		syscall.Dup2(int(f.Fd()), int(os.Stderr.Fd()))
	}

	// Redis
	parts := strings.Split(conn, "/")
	host := parts[0]
	db := 0
	if len(parts) >= 2 {
		db, _ = strconv.Atoi(parts[1])
	}
	cfg := redis.Config{Database: db, Address: host, PoolCapacity: 4}
	connection = redis.NewClient(cfg)
	defer connection.Close()

	// Routing
	m := pat.New()
	m.Get("/status", http.HandlerFunc(Status))
	m.Get("/img/:encoded_url/:filename", http.HandlerFunc(Img))
	m.Get("/img/:encoded_url", http.HandlerFunc(Img))
	m.Get("/avatars/:encoded_url/:filename", http.HandlerFunc(Avatar))
	m.Get("/avatars/:encoded_url", http.HandlerFunc(Avatar))
	http.Handle("/", m)

	// Start the HTTP server
	log.Printf("Listening on http://%s/\n", addr)
	err := http.ListenAndServe(addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


<commit_before>/*
 * Provide IRC client functionality.
 *
 * I would like to be able to write bots using this package.
 */

package irc

import (
	"bufio"
	"crypto/tls"
	"fmt"
	"log"
	"net"
	"strings"
	"time"
)

// Conn holds an IRC connection.
type Conn struct {
	// Nick is the desired nickname.
	Nick string

	// Name is the realname to use.
	Name string

	// Ident is the ident portion to use.
	Ident string

	// Host is the IP/hostname of the IRC server to connect to.
	Host string

	// Port is the port of the host of the IRC server to connect to.
	Port int

	// TLS toggles whether we connect with TLS/SSL or not.
	TLS bool

	// connected: Whether currently connected or not
	connected bool

	// conn: The connection if we are actively connected.
	conn net.Conn

	// rw: Read/write handle to the connection
	rw *bufio.ReadWriter

	// actualNick: The nick we have if we are currently connected. The requested
	// nick may not always be available.
	actualNick string
}

// timeoutTime is how long we wait on network I/O.
const timeoutTime = 5 * time.Minute

// timeoutConnect is how long we wait for connection attempts to time out.
const timeoutConnect = 30 * time.Second

var Hooks []func(*Conn, Message)

// Connect attempts to connect to a server.
func (c *Conn) Connect() error {
	if c.TLS {
		dialer := &net.Dialer{Timeout: timeoutConnect}
		conn, err := tls.DialWithDialer(dialer, "tcp",
			fmt.Sprintf("%s:%d", c.Host, c.Port),
			&tls.Config{
				// Typically IRC servers won't have valid certs.
				InsecureSkipVerify: true,
			})

		if err != nil {
			return err
		}

		c.conn = conn
		c.connected = true
	} else {
		conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", c.Host, c.Port),
			timeoutConnect)

		if err != nil {
			return err
		}

		c.conn = conn
		c.connected = true
	}

	c.rw = bufio.NewReadWriter(bufio.NewReader(c.conn), bufio.NewWriter(c.conn))

	err := c.greet()
	if err != nil {
		return err
	}

	return nil
}

// greet runs connection initiation (NICK, USER)
func (c *Conn) greet() error {
	err := c.write(fmt.Sprintf("NICK %s\r\n", c.Nick))
	if err != nil {
		return fmt.Errorf("Failed to send NICK: %s", err.Error())
	}

	err = c.write(fmt.Sprintf("USER %s 0 * :%s\r\n", c.Ident, c.Name))
	if err != nil {
		return fmt.Errorf("Failed to send NICK: %s", err.Error())
	}

	for {
		line, err := c.read()
		if err != nil {
			return err
		}

		msg, err := parseMessage(line)
		if err != nil {
			return err
		}

		// Ignore
		if msg.Command == "NOTICE" {
			continue
		}

		// Look for numeric reply 1. This is RPL_WELCOME welcoming our connection.
		if msg.Command == "001" {
			log.Printf("Got welcome!")
			return nil
		}
	}
}

// Loop enters a loop reading from the server.
//
// We maintain the IRC connection.
//
// Hook events will fire.
func (c *Conn) Loop() error {
	for {
		if !c.connected {
			err := c.Connect()
			return err
			if err != nil {
				return err
			}
		}

		line, err := c.read()
		if err != nil {
			return err
		}

		msg, err := parseMessage(line)
		if err != nil {
			return err
		}

		if msg.Command == "PING" {
			err = c.write(fmt.Sprintf("PONG %s\r\n", msg.Params[0]))
			if err != nil {
				return fmt.Errorf("Failed to send PONG: %s", err.Error())
			}
			log.Printf("Sent PONG.")
		}

		c.hooks(msg)
	}
}

// hooks calls each registered IRC package hook.
func (c *Conn) hooks(message Message) {
	for _, hook := range Hooks {
		hook(c, message)
	}
}

// Join joins a channel.
func (c *Conn) Join(name string) error {
	return c.write(fmt.Sprintf("JOIN %s\r\n", name))
}

// Message sends a message.
//
// If the message is too long for a single line, then it will be split over
// several lines.
func (c *Conn) Message(target string, message string) error {

	// 512 is the maximum IRC protocol length.
	maxMessage := 512

	// Number of overhead bytes.
	overhead := len("PRIVMSG ") + len(" :") + len("\r\n")

	for i := 0; i < len(message); i += maxMessage - overhead {
		endIndex := i + maxMessage - overhead
		if endIndex > len(message) {
			endIndex = len(message)
		}
		piece := message[i:endIndex]

		command := fmt.Sprintf("PRIVMSG %s :%s\r\n", target, piece)
		err := c.write(command)
		if err != nil {
			return nil
		}
	}

	return nil
}

// Quit sends a quit.
func (c *Conn) Quit(message string) error {
	return c.write(fmt.Sprintf("QUIT :%s\r\n", message))
}

// read reads a message from the connection.
func (c *Conn) read() (string, error) {
	err := c.conn.SetDeadline(time.Now().Add(timeoutTime))
	if err != nil {
		return "", fmt.Errorf("Unable to set deadline: %s", err)
	}

	line, err := c.rw.ReadString('\n')
	if err != nil {
		return "", fmt.Errorf("Unable to read: %s", err)
	}

	log.Printf("Read: %s", strings.TrimSpace(line))

	return line, nil
}

// write writes a string to the connection
func (c *Conn) write(s string) error {
	err := c.conn.SetDeadline(time.Now().Add(timeoutTime))
	if err != nil {
		return fmt.Errorf("Unable to set deadline: %s", err)
	}

	sz, err := c.rw.WriteString(s)
	if err != nil {
		return fmt.Errorf("Unable to write: %s", err)
	}

	if sz != len(s) {
		return fmt.Errorf("Short write")
	}

	err = c.rw.Flush()
	if err != nil {
		return fmt.Errorf("Flush error: %s", err)
	}

	log.Printf("Sent: %s", strings.TrimSpace(s))

	return nil
}
<commit_msg>irc: Trim down max message size<commit_after>/*
 * Provide IRC client functionality.
 *
 * I would like to be able to write bots using this package.
 */

package irc

import (
	"bufio"
	"crypto/tls"
	"fmt"
	"log"
	"net"
	"strings"
	"time"
)

// Conn holds an IRC connection.
type Conn struct {
	// Nick is the desired nickname.
	Nick string

	// Name is the realname to use.
	Name string

	// Ident is the ident portion to use.
	Ident string

	// Host is the IP/hostname of the IRC server to connect to.
	Host string

	// Port is the port of the host of the IRC server to connect to.
	Port int

	// TLS toggles whether we connect with TLS/SSL or not.
	TLS bool

	// connected: Whether currently connected or not
	connected bool

	// conn: The connection if we are actively connected.
	conn net.Conn

	// rw: Read/write handle to the connection
	rw *bufio.ReadWriter

	// actualNick: The nick we have if we are currently connected. The requested
	// nick may not always be available.
	actualNick string
}

// timeoutTime is how long we wait on network I/O.
const timeoutTime = 5 * time.Minute

// timeoutConnect is how long we wait for connection attempts to time out.
const timeoutConnect = 30 * time.Second

var Hooks []func(*Conn, Message)

// Connect attempts to connect to a server.
func (c *Conn) Connect() error {
	if c.TLS {
		dialer := &net.Dialer{Timeout: timeoutConnect}
		conn, err := tls.DialWithDialer(dialer, "tcp",
			fmt.Sprintf("%s:%d", c.Host, c.Port),
			&tls.Config{
				// Typically IRC servers won't have valid certs.
				InsecureSkipVerify: true,
			})

		if err != nil {
			return err
		}

		c.conn = conn
		c.connected = true
	} else {
		conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", c.Host, c.Port),
			timeoutConnect)

		if err != nil {
			return err
		}

		c.conn = conn
		c.connected = true
	}

	c.rw = bufio.NewReadWriter(bufio.NewReader(c.conn), bufio.NewWriter(c.conn))

	err := c.greet()
	if err != nil {
		return err
	}

	return nil
}

// greet runs connection initiation (NICK, USER)
func (c *Conn) greet() error {
	err := c.write(fmt.Sprintf("NICK %s\r\n", c.Nick))
	if err != nil {
		return fmt.Errorf("Failed to send NICK: %s", err.Error())
	}

	err = c.write(fmt.Sprintf("USER %s 0 * :%s\r\n", c.Ident, c.Name))
	if err != nil {
		return fmt.Errorf("Failed to send NICK: %s", err.Error())
	}

	for {
		line, err := c.read()
		if err != nil {
			return err
		}

		msg, err := parseMessage(line)
		if err != nil {
			return err
		}

		// Ignore
		if msg.Command == "NOTICE" {
			continue
		}

		// Look for numeric reply 1. This is RPL_WELCOME welcoming our connection.
		if msg.Command == "001" {
			log.Printf("Got welcome!")
			return nil
		}
	}
}

// Loop enters a loop reading from the server.
//
// We maintain the IRC connection.
//
// Hook events will fire.
func (c *Conn) Loop() error {
	for {
		if !c.connected {
			err := c.Connect()
			return err
			if err != nil {
				return err
			}
		}

		line, err := c.read()
		if err != nil {
			return err
		}

		msg, err := parseMessage(line)
		if err != nil {
			return err
		}

		if msg.Command == "PING" {
			err = c.write(fmt.Sprintf("PONG %s\r\n", msg.Params[0]))
			if err != nil {
				return fmt.Errorf("Failed to send PONG: %s", err.Error())
			}
			log.Printf("Sent PONG.")
		}

		c.hooks(msg)
	}
}

// hooks calls each registered IRC package hook.
func (c *Conn) hooks(message Message) {
	for _, hook := range Hooks {
		hook(c, message)
	}
}

// Join joins a channel.
func (c *Conn) Join(name string) error {
	return c.write(fmt.Sprintf("JOIN %s\r\n", name))
}

// Message sends a message.
//
// If the message is too long for a single line, then it will be split over
// several lines.
func (c *Conn) Message(target string, message string) error {

	// 512 is the maximum IRC protocol length.
	// However, user and host takes up some of that. Let's cut down a bit.
	// This is arbitrary.
	maxMessage := 412

	// Number of overhead bytes.
	overhead := len("PRIVMSG ") + len(" :") + len("\r\n")

	for i := 0; i < len(message); i += maxMessage - overhead {
		endIndex := i + maxMessage - overhead
		if endIndex > len(message) {
			endIndex = len(message)
		}
		piece := message[i:endIndex]

		command := fmt.Sprintf("PRIVMSG %s :%s\r\n", target, piece)
		err := c.write(command)
		if err != nil {
			return nil
		}
	}

	return nil
}

// Quit sends a quit.
func (c *Conn) Quit(message string) error {
	return c.write(fmt.Sprintf("QUIT :%s\r\n", message))
}

// read reads a message from the connection.
func (c *Conn) read() (string, error) {
	err := c.conn.SetDeadline(time.Now().Add(timeoutTime))
	if err != nil {
		return "", fmt.Errorf("Unable to set deadline: %s", err)
	}

	line, err := c.rw.ReadString('\n')
	if err != nil {
		return "", fmt.Errorf("Unable to read: %s", err)
	}

	log.Printf("Read: %s", strings.TrimSpace(line))

	return line, nil
}

// write writes a string to the connection
func (c *Conn) write(s string) error {
	err := c.conn.SetDeadline(time.Now().Add(timeoutTime))
	if err != nil {
		return fmt.Errorf("Unable to set deadline: %s", err)
	}

	sz, err := c.rw.WriteString(s)
	if err != nil {
		return fmt.Errorf("Unable to write: %s", err)
	}

	if sz != len(s) {
		return fmt.Errorf("Short write")
	}

	err = c.rw.Flush()
	if err != nil {
		return fmt.Errorf("Flush error: %s", err)
	}

	log.Printf("Sent: %s", strings.TrimSpace(s))

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"github.com/fzzy/radix/redis"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"runtime/debug"
	"strings"
	"sync"
	"time"
)

var (
	funcMap    map[string]command
	db         redis.Client
	regexpCmds []*regexp.Regexp
	config     JSONconfig
)

type JSONconfig struct {
	Nick     string
	Pass     string
	Hostname string
}

//output err
func errOut(err error, quit chan bool) {
	fmt.Println("ERROR: ", err.Error())
	debug.PrintStack()
	if err.Error() == "EOF" {
		fmt.Println("EXITING")
		for i := 0; i < 2; i++ {
			quit <- true
		}
		fmt.Println("QUITS SENT")
	}
}

//take input from srvChan and send to server
func writeToServer(socket *tls.Conn, srvChan chan string, wg *sync.WaitGroup, quit chan bool) {
	defer wg.Done()
	defer fmt.Println("WTS") //debug

	w := bufio.NewWriter(socket)
	_, err := w.WriteString(<-srvChan + "\r\n")
	if err == nil {
		err = w.Flush()
	}
	//send all lines in srvChan to server
	for err == nil {
		select {
		case <-quit: //exit if indicated
			return
		case str := <-srvChan:
			_, err = w.WriteString(str + "\r\n")
			if err == nil {
				err = w.Flush()
			}
		}
	}

	//print error and exit
	if err != nil {
		errOut(err, quit)
		socket.Close()
	}
}

//take input from connection and send to console channel
func readFromServer(socket *tls.Conn, srvChan chan string, wg *sync.WaitGroup, quit chan bool) {
	defer wg.Done()
	defer fmt.Println("RFS")

	socket.SetReadDeadline(time.Time{})
	r := bufio.NewReader(socket)
	line, line_err := r.ReadString('\n')
	for ; line_err == nil; line, line_err = r.ReadString('\n') {
		select {
		case <-quit:
			return
		default:
			srvChan <- strings.TrimSpace(line)
		}
	}
	if line_err != nil {
		errOut(line_err, quit)
		socket.Close()
	}
}

func writeToConsole(readChan chan string, writeChan chan string, wg *sync.WaitGroup, quit chan bool) {
	defer wg.Done()
	defer fmt.Println("WTC") //debug

	pingRegex := regexp.MustCompile("^PING (.*)")
	questionRegex := regexp.MustCompile(`^:(\S*?)!(\S*?)@(\S*?) PRIVMSG (\S*) :` + config.Nick + `:.*\?`)

	//read every line from the server chan and print to console
	for {
		select {
		case <-quit: //exit if indicated
			return
		case line := <-readChan:
			fmt.Println(line)
			if match := pingRegex.FindStringSubmatch(line); match != nil {
				//respond to PING from server
				writeChan <- ("PONG " + match[1])
				fmt.Println("PONG", match[1]) //put to console
			} else if match := questionRegex.FindStringSubmatch(line); match != nil {
				go yesNo(writeChan, match[4], match[1], match[3])
			} else {
				var match []string
				for _, regexp := range regexpCmds {
					if match = regexp.FindStringSubmatch(line); match != nil {
						cmdArgs := strings.Fields(match[5])
						if cmd, valid := funcMap[cmdArgs[0]]; valid {
							if match[4] == config.Nick {
								match[4] = match[1]
							}
							go cmd(writeChan, match[4], match[1], match[3], cmdArgs[1:])
						}
						break
					}
				}
			}
		}
	}
}

//read input from console and send to srvChan
func readFromConsole(srvChan chan string, wg *sync.WaitGroup, quit chan bool, error chan bool) {
	defer wg.Done()
	defer fmt.Println("RFC") //debug

	in := bufio.NewScanner(os.Stdin)
	//read all text from console, send it to srvChan to be sent to server
	for in.Scan() {
		str := in.Text()
		srvChan <- str
		if strings.TrimSpace(str) == "QUIT" { //exit and indicate upon reading QUIT
			error <- true
			return
		}
	}

	//print error and exit
	if err := in.Err(); err != nil {
		error <- true
		errOut(err, quit)
	}
}

func main() {
	rand.Seed(time.Now().Unix())
	configFile, err := ioutil.ReadFile("config.json")
	if err == nil {
		json.Unmarshal(configFile, &config)
	} else {
		config = JSONconfig{"yaircb", "", "*"}
	}

	regexpCmds = make([]*regexp.Regexp, 3)
	regexpCmds[0] = regexp.MustCompile(`^:(\S*?)!(\S*?)@(\S*?) PRIVMSG (\S*) :` + config.Nick + `:\s*(.*)`)
	regexpCmds[1] = regexp.MustCompile(`^:(\S*)?!(\S*)?@(\S*)? PRIVMSG (\S*) :\s*\+(.*)`)
	regexpCmds[2] = regexp.MustCompile(`^:(\S*)?!(\S*)?@(\S*)? PRIVMSG (` + config.Nick + `) :\s*(.*)`)

	funcMap = initMap()
	initCmdRedis()

	db, err := redis.Dial("tcp", "127.0.0.1:6379")
	if err != nil {
		fmt.Println(err)
		return
	}
	reply, err := db.Cmd("get", "heydabop").Bytes()
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(reply))

	initWebRedis()
	http.Handle("/resources/", http.StripPrefix("/resources/", http.FileServer(http.Dir("resources"))))
	http.HandleFunc("/register/", registerHandler)
	http.HandleFunc("/login/", loginHandler)
	http.HandleFunc("/loginCheck/", loginCheckHandler)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/save/", saveHandler)
	http.HandleFunc("/user/", userHandler)
	go http.ListenAndServeTLS(":8080", "ssl.crt", "ssl.pem", nil)

	var conns uint16
	writeChan := make(chan string) //used to send strings from readFromConsole to writeToServer
	readChan := make(chan string)  //send strings from readFromServer to writeToConsole
	//wgSrv for goroutines to/from sever, wg for readFromConsole
	var wgSrv, wg sync.WaitGroup
	quit := make(chan bool, 2)  //used to indicate server to/from goroutines need to exit
	error := make(chan bool, 1) //used to indicate readFromConsole exited
	//initiate connection
	wg.Add(2)
	go readFromConsole(writeChan, &wg, quit, error)   //doesnt get restarted on connection EOF
	go writeToConsole(readChan, writeChan, &wg, quit) //doesnt get restarted on connection EOF
connectionLoop:
	for ; ; conns++ {
		select {
		case <-error: //if readFromConsole got a "QUIT", exit program
			break connectionLoop
		default: //otherwise restart connections
			if conns == 0 {
				fmt.Println("STARTING...")
			} else {
				fmt.Println("RESTARTING...")
			}
			socket, err := tls.Dial("tcp", "chat.freenode.net:6697", nil)
			if err != nil {
				errOut(err, quit)
				return
			}
			//make writer/reader to/from server
			//send initial IRC messages, NICK and USER
			w := bufio.NewWriter(socket)
			_, err = w.WriteString("NICK " + config.Nick + "\r\n")
			if err == nil {
				err = w.Flush()
			}
			fmt.Print("NICK " + config.Nick + "\r\n")
			if err != nil {
				errOut(err, quit)
			}
			_, err = w.WriteString("USER " + config.Nick + " " + config.Hostname + " * :yaircb\r\n")
			if err == nil {
				err = w.Flush()
			}
			fmt.Print("USER " + config.Nick + " " + config.Hostname + " * :yaircb\r\n")
			if err != nil {
				errOut(err, quit)
			}
			wgSrv.Add(1)
			//launch routine to write server output to console
			go readFromServer(socket, readChan, &wgSrv, quit)
			//join first channel
			/*err = socket.Writer.PrintfLine("JOIN #ttestt")
			if err != nil {
				errOut(err, quit)
			}*/
			_, err = w.WriteString("PRIVMSG NickServ :IDENTIFY " + config.Pass + "\r\n")
			wgSrv.Add(1)
			//launch routine to send to server and get input from console
			go writeToServer(socket, writeChan, &wgSrv, quit)
			wgSrv.Wait()
		}
	}
	wg.Wait()
}
<commit_msg>now attempts plaintext connection if TLS fails<commit_after>package main

import (
	"bufio"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"github.com/fzzy/radix/redis"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/textproto"
	"os"
	"regexp"
	"runtime/debug"
	"strings"
	"sync"
	"time"
)

var (
	funcMap    map[string]command
	db         redis.Client
	regexpCmds []*regexp.Regexp
	config     JSONconfig
)

type JSONconfig struct {
	Nick     string
	Pass     string
	Hostname string
}

//output err
func errOut(err error, quit chan bool) {
	fmt.Println("ERROR: ", err.Error())
	debug.PrintStack()
	if err.Error() == "EOF" {
		fmt.Println("EXITING")
		for i := 0; i < 2; i++ {
			quit <- true
		}
		fmt.Println("QUITS SENT")
	}
}

//take input from srvChan and send to server
func writeToServer(w *bufio.Writer, srvChan chan string, wg *sync.WaitGroup, quit chan bool) {
	defer wg.Done()
	defer fmt.Println("WTS") //debug

	_, err := w.WriteString(<-srvChan + "\r\n")
	if err == nil {
		err = w.Flush()
	}
	//send all lines in srvChan to server
	for err == nil {
		select {
		case <-quit: //exit if indicated
			return
		case str := <-srvChan:
			_, err = w.WriteString(str + "\r\n")
			if err == nil {
				err = w.Flush()
			}
		}
	}

	//print error and exit
	if err != nil {
		errOut(err, quit)
	}
}

//take input from connection and send to console channel
func readFromServer(r *bufio.Reader, srvChan chan string, wg *sync.WaitGroup, quit chan bool) {
	defer wg.Done()
	defer fmt.Println("RFS")

	line, line_err := r.ReadString('\n')
	for ; line_err == nil; line, line_err = r.ReadString('\n') {
		select {
		case <-quit:
			return
		default:
			srvChan <- strings.TrimSpace(line)
		}
	}
	if line_err != nil {
		errOut(line_err, quit)
	}
}

func writeToConsole(readChan chan string, writeChan chan string, wg *sync.WaitGroup, quit chan bool) {
	defer wg.Done()
	defer fmt.Println("WTC") //debug

	pingRegex := regexp.MustCompile("^PING (.*)")
	questionRegex := regexp.MustCompile(`^:(\S*?)!(\S*?)@(\S*?) PRIVMSG (\S*) :` + config.Nick + `:.*\?`)

	//read every line from the server chan and print to console
	for {
		select {
		case <-quit: //exit if indicated
			return
		case line := <-readChan:
			fmt.Println(line)
			if match := pingRegex.FindStringSubmatch(line); match != nil {
				//respond to PING from server
				writeChan <- ("PONG " + match[1])
				fmt.Println("PONG", match[1]) //put to console
			} else if match := questionRegex.FindStringSubmatch(line); match != nil {
				go yesNo(writeChan, match[4], match[1], match[3])
			} else {
				var match []string
				for _, regexp := range regexpCmds {
					if match = regexp.FindStringSubmatch(line); match != nil {
						cmdArgs := strings.Fields(match[5])
						if cmd, valid := funcMap[cmdArgs[0]]; valid {
							if match[4] == config.Nick {
								match[4] = match[1]
							}
							go cmd(writeChan, match[4], match[1], match[3], cmdArgs[1:])
						}
						break
					}
				}
			}
		}
	}
}

//read input from console and send to srvChan
func readFromConsole(srvChan chan string, wg *sync.WaitGroup, quit chan bool, error chan bool) {
	defer wg.Done()
	defer fmt.Println("RFC") //debug

	in := bufio.NewScanner(os.Stdin)
	//read all text from console, send it to srvChan to be sent to server
	for in.Scan() {
		str := in.Text()
		srvChan <- str
		if strings.TrimSpace(str) == "QUIT" { //exit and indicate upon reading QUIT
			error <- true
			return
		}
	}

	//print error and exit
	if err := in.Err(); err != nil {
		error <- true
		errOut(err, quit)
	}
}

func main() {
	rand.Seed(time.Now().Unix())
	configFile, err := ioutil.ReadFile("config.json")
	if err == nil {
		json.Unmarshal(configFile, &config)
	} else {
		config = JSONconfig{"yaircb", "", "*"}
	}

	regexpCmds = make([]*regexp.Regexp, 3)
	regexpCmds[0] = regexp.MustCompile(`^:(\S*?)!(\S*?)@(\S*?) PRIVMSG (\S*) :` + config.Nick + `:\s*(.*)`)
	regexpCmds[1] = regexp.MustCompile(`^:(\S*)?!(\S*)?@(\S*)? PRIVMSG (\S*) :\s*\+(.*)`)
	regexpCmds[2] = regexp.MustCompile(`^:(\S*)?!(\S*)?@(\S*)? PRIVMSG (` + config.Nick + `) :\s*(.*)`)

	funcMap = initMap()
	initCmdRedis()

	db, err := redis.Dial("tcp", "127.0.0.1:6379")
	if err != nil {
		fmt.Println(err)
		return
	}
	reply, err := db.Cmd("get", "heydabop").Bytes()
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(reply))

	initWebRedis()
	http.Handle("/resources/", http.StripPrefix("/resources/", http.FileServer(http.Dir("resources"))))
	http.HandleFunc("/register/", registerHandler)
	http.HandleFunc("/login/", loginHandler)
	http.HandleFunc("/loginCheck/", loginCheckHandler)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/save/", saveHandler)
	http.HandleFunc("/user/", userHandler)
	go http.ListenAndServeTLS(":8080", "ssl.crt", "ssl.pem", nil)

	var conns uint16
	writeChan := make(chan string) //used to send strings from readFromConsole to writeToServer
	readChan := make(chan string)  //send strings from readFromServer to writeToConsole
	//wgSrv for goroutines to/from sever, wg for readFromConsole
	var wgSrv, wg sync.WaitGroup
	quit := make(chan bool, 2)  //used to indicate server to/from goroutines need to exit
	error := make(chan bool, 1) //used to indicate readFromConsole exited
	//initiate connection
	wg.Add(2)
	go readFromConsole(writeChan, &wg, quit, error)   //doesnt get restarted on connection EOF
	go writeToConsole(readChan, writeChan, &wg, quit) //doesnt get restarted on connection EOF
connectionLoop:
	for ; ; conns++ {
		select {
		case <-error: //if readFromConsole got a "QUIT", exit program
			break connectionLoop
		default: //otherwise restart connections
			if conns == 0 {
				fmt.Println("STARTING...")
			} else {
				fmt.Println("RESTARTING...")
			}
			var socketRead *bufio.Reader
			var socketWrite *bufio.Writer
			sslSocket, err := tls.Dial("tcp", "chat.freenode.net:6697", nil)
			if err == nil {
				sslSocket.SetReadDeadline(time.Time{})
				socketWrite = bufio.NewWriter(sslSocket)
				socketRead = bufio.NewReader(sslSocket)
			} else {
				socket, err := textproto.Dial("tcp", "chat.freenode.net:6667")
				if err != nil {
					errOut(err, quit)
					return
				}
				socketWrite = socket.Writer.W
				socketRead = socket.Reader.R
			}
			//make writer/reader to/from server
			//send initial IRC messages, NICK and USER
			_, err = socketWrite.WriteString("NICK " + config.Nick + "\r\n")
			if err == nil {
				err = socketWrite.Flush()
			}
			fmt.Print("NICK " + config.Nick + "\r\n")
			if err != nil {
				errOut(err, quit)
			}
			_, err = socketWrite.WriteString("USER " + config.Nick + " " + config.Hostname + " * :yaircb\r\n")
			if err == nil {
				err = socketWrite.Flush()
			}
			fmt.Print("USER " + config.Nick + " " + config.Hostname + " * :yaircb\r\n")
			if err != nil {
				errOut(err, quit)
			}
			wgSrv.Add(1)
			//launch routine to write server output to console
			go readFromServer(socketRead, readChan, &wgSrv, quit)
			//join first channel
			/*err = socket.Writer.PrintfLine("JOIN #ttestt")
			if err != nil {
				errOut(err, quit)
			}*/
			_, err = socketWrite.WriteString("PRIVMSG NickServ :IDENTIFY " + config.Pass + "\r\n")
			wgSrv.Add(1)
			//launch routine to send to server and get input from console
			go writeToServer(socketWrite, writeChan, &wgSrv, quit)
			wgSrv.Wait()
		}
	}
	wg.Wait()
}
<|endoftext|>


<commit_before>package irg

import (
	"fmt"
	"github.com/bcart3r/irg/irc"
	"regexp"
)

type Bot struct {
	Nick, Name string
	Events     chan string
	Conn       *irc.Irc
}

func (b *Bot) Login(nick, name string) {
	b.setNickName(nick, name)
	b.Conn.W <- "NICK " + b.Nick
	b.Conn.W <- "NAME " + b.Nick + " 0 * :" + b.Name
}

func (b *Bot) setNickName(nick, name string) {
	b.Nick = nick
	b.Name = name
}

func Connect(server string) *Bot {
	conn := irc.Connect(server)
	events := make(chan string, 1000)

	conn.ReadHandler()
	conn.WriteHandler()

	return &Bot{"GoBot", "GoBot", events, conn}
}

func (b *Bot) RunLoop() {
	for {
		ln := <-b.Conn.R
		fmt.Print(ln)
		if regexp.MustCompile("^PING").Match([]byte(ln)) {
			b.Conn.W <- "PONG " + regexp.MustCompile(":.*").FindString(ln)
		}
	}
}
<commit_msg>move handlers to run loop<commit_after>package irg

import (
	"fmt"
	"github.com/bcart3r/irg/irc"
	"regexp"
)

type Bot struct {
	Nick, Name string
	Events     chan string
	Conn       *irc.Irc
}

func (b *Bot) Login(nick, name string) {
	b.setNickName(nick, name)
	b.Conn.W <- "NICK " + b.Nick
	b.Conn.W <- "NAME " + b.Nick + " 0 * :" + b.Name
}

func (b *Bot) setNickName(nick, name string) {
	b.Nick = nick
	b.Name = name
}

func Connect(server string) *Bot {
	conn := irc.Connect(server)
	events := make(chan string, 1000)

	return &Bot{"GoBot", "GoBot", events, conn}
}

func (b *Bot) RunLoop() {
	b.Conn.ReadHandler()
	b.Conn.WriteHandler()

	for {
		ln := <-b.Conn.R
		fmt.Print(ln)
		if regexp.MustCompile("^PING").Match([]byte(ln)) {
			b.Conn.W <- "PONG " + regexp.MustCompile(":.*").FindString(ln)
		}
	}
}
<|endoftext|>


<commit_before>// j2x package - mirror of x2j package
//	Marshal XML docs from arbitrary JSON string and map[string]interface{} variables.
// Copyright 2013 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
/*	Marshal dynamic / arbitrary XML docs from arbitrary JSON string and map[string]interface{} variables.

	Compliments the x2j package functions.

	Uses x2j conventions:
		- Keys that begin with a hyphen, '-', are treated as attributes.
		- The "#text" key is treated as the value for a simple element.

	Map values that are not standard JSON types - can be a structure, etc. - are marshal'd using xml.Marshal().
	However, attribute keys are restricted to string, numeric, or boolean types.

	If the map[string]interface{} has a single key, it is used as the XML root tag.  If it doesn't have
	a single key, then a root tag - rootTag - must be provided or the default root tag value is used.

	EMPTY ELEMENT ENCODING

	Empty (nil) elements or elements with only attributes are encoded as "<tag .../>".  The standard library
	encoding/xml package encodes them as "<tag ...></tag>".  If you're marshaling a map with structure values
	and want a consistent syntax, use the xml_marshal hack of the standard library that conforms encoding/xml
	to the j2x convention.
*/
package j2x

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
)

const (
	DefaultRootTag = "doc"
)

// Extends xml.Marshal() to handle JSON and map[string]interface{} types.
//	This is the inverse of x2j.Unmarshal().
//	Strings are interpreted as JSON strings; use xml.Marshal() to marshal
//	a string as "<string>...</string>" - the standard package handling.
//	Follows xml.Marshal handling of types except for string and map[string]interface{}
//	values. For more generalized marshal'ing use MapToDoc().
//	See MapToDoc() for encoding rules.
func Marshal(v interface{}, rootTag ...string) ([]byte, error) {
	switch v.(type) {
	case string:
		xmlString, err := JsonToDoc(v.(string), rootTag...)
		return []byte(xmlString), err
	case map[string]interface{}:
		xmlString, err := MapToDoc(v.(map[string]interface{}), rootTag...)
		return []byte(xmlString), err
	}
	return xml.Marshal(v)
}

// Encode a JSON string as XML.  The inverse of x2j.DocToJson().
//	See MapToDoc() for encoding rules.
func JsonToDoc(jsonString string, rootTag ...string) (string, error) {
	m := make(map[string]interface{}, 0)
	if err := json.Unmarshal([]byte(jsonString), &m); err != nil {
		return "", err
	}
	return MapToDoc(m, rootTag...)
}

// Encode a map[string]interface{} variable as XML.  The inverse of x2j.DocToMap().
// The following rules apply.
//    - The key label "#text" is treated as the value for a simple element with attributes.
//    - Map keys that begin with a hyphen, '-', are interpreted as attributes.
//      It is an error if the attribute doesn't have a []byte, string, number, or boolean value.
//    - Map value type encoding:
//          > string, bool, float64, int, int32, int64, float32: per "%v" formating
//          > []bool, []uint8: by casting to string
//          > structures, etc.: handed to xml.Marshal() - if there is an error, the element
//            value is "UNKNOWN"
//    - Elements with only attribute values or are null are terminated using "/>".
//    - If len(m) == 1 and no rootTag is provided, then the map key is used as the root tag.
//      Thus, `{ "key":"value" }` encodes as `<key>value</key>`.
func MapToDoc(m map[string]interface{}, rootTag ...string) (string, error) {
	var err error
	s := new(string)

	if len(m) == 1 && len(rootTag) == 0 {
		for key, value := range m {
			if _, ok := value.([]interface{}); ok {
				err = mapToDoc(s, DefaultRootTag, m)
			} else {
				err = mapToDoc(s, key, value)
			}
		}
	} else if len(rootTag) == 1 {
		err = mapToDoc(s, rootTag[0], m)
	} else {
		err = mapToDoc(s, DefaultRootTag, m)
	}
	return *s, err
}

// where the work actually happens
// returns an error if an attribute is not atomic
func mapToDoc(s *string, key string, value interface{}) error {
	var endTag bool

	if _, ok := value.([]interface{}); !ok {
		*s += `<` + key
	}
	switch value.(type) {
	case map[string]interface{}:
		vv := value.(map[string]interface{})
		lenvv := len(vv)
		// scan out attributes - keys have prepended hyphen, '-'
		var cntAttr int
		for k, v := range vv {
			if k[:1] == "-" {
				switch v.(type) {
				case string, float64, bool, int, int32, int64, float32:
					*s += ` ` + k[1:] + `="` + fmt.Sprintf("%v", v) + `"`
					cntAttr++
				case []byte:		// allow standard xml pkg []byte transform, as below
					*s += ` ` + k[1:] + `="` + fmt.Sprintf("%v",string(v.([]byte))) + `"`
					cntAttr++
				default:
					return errors.New("invalid attribute value for: " + k)
				}
			}
		}
		// only attributes?
		if cntAttr == lenvv {
			break
		}
		// simple element? Note: '#text" is an invalid XML tag.
		if v, ok := vv["#text"]; ok {
			if cntAttr+1 < lenvv {
				return errors.New("#text key occurs with other non-attribute keys")
			}
			*s += ">" + fmt.Sprintf("%v", v)
			endTag = true
			break
		}
		// close tag with possible attributes
		*s += ">"
		// something more complex
		for k, v := range vv {
			if k[:1] == "-" {
				continue
			}
			mapToDoc(s, k, v)
		}
		endTag = true
	case []interface{}:
		for _, v := range value.([]interface{}) {
			mapToDoc(s, key, v)
		}
		return nil
	case nil:
		// terminate the tag
		break
	default: // handle anything - even goofy stuff
		var tmp string
		switch value.(type) {
		case string, float64, bool, int, int32, int64, float32:
			tmp = fmt.Sprintf("%v", value)
		case []byte:			// NOTE: byte is just an alias for uint8
			// similar to how xml.Marshal handles []byte structure members
			tmp = fmt.Sprintf("%v", string(value.([]byte)))
		default:
			v, err := xml.Marshal(value)
			if err != nil {
				tmp = "UNKNOWN"
			} else {
				tmp = string(v)
			}
		}
		*s += ">" + tmp
		endTag = true
	}

	if endTag {
		*s += "</" + key + ">"
	} else {
		*s += "/>"
	}
	return nil
}
<commit_msg>documentation<commit_after>// Mirror of x2j package. Marshal XML docs from JSON string and map[string]interface{} variables.
// Copyright 2013 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
/*	Marshal dynamic / arbitrary XML docs from arbitrary JSON string and map[string]interface{} variables.

	Compliments the x2j package functions.

	Uses x2j conventions:
		- Keys that begin with a hyphen, '-', are treated as attributes.
		- The "#text" key is treated as the value for a simple element.

	Map values that are not standard JSON types - can be a structure, etc. - are marshal'd using xml.Marshal().
	However, attribute keys are restricted to string, numeric, or boolean types.

	If the map[string]interface{} has a single key, it is used as the XML root tag.  If it doesn't have
	a single key, then a root tag - rootTag - must be provided or the default root tag value is used.

	EMPTY ELEMENT ENCODING

	Empty (nil) elements or elements with only attributes are encoded as "<tag .../>".  The standard library
	encoding/xml package encodes them as "<tag ...></tag>".  If you're marshaling a map with structure values
	and want a consistent syntax, use the xml_marshal hack of the standard library that conforms encoding/xml
	to the j2x convention.
*/
package j2x

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
)

const (
	DefaultRootTag = "doc"
)

// Extends xml.Marshal() to handle JSON and map[string]interface{} types.
//	This is the inverse of x2j.Unmarshal().
//	Strings are interpreted as JSON strings; use xml.Marshal() to marshal
//	a string as "<string>...</string>" - the standard package handling.
//	Follows xml.Marshal handling of types except for string and map[string]interface{}
//	values. For more generalized marshal'ing use MapToDoc().
//	See MapToDoc() for encoding rules.
func Marshal(v interface{}, rootTag ...string) ([]byte, error) {
	switch v.(type) {
	case string:
		xmlString, err := JsonToDoc(v.(string), rootTag...)
		return []byte(xmlString), err
	case map[string]interface{}:
		xmlString, err := MapToDoc(v.(map[string]interface{}), rootTag...)
		return []byte(xmlString), err
	}
	return xml.Marshal(v)
}

// Encode a JSON string as XML.  The inverse of x2j.DocToJson().
//	See MapToDoc() for encoding rules.
func JsonToDoc(jsonString string, rootTag ...string) (string, error) {
	m := make(map[string]interface{}, 0)
	if err := json.Unmarshal([]byte(jsonString), &m); err != nil {
		return "", err
	}
	return MapToDoc(m, rootTag...)
}

// Encode a map[string]interface{} variable as XML.  The inverse of x2j.DocToMap().
// The following rules apply.
//    - The key label "#text" is treated as the value for a simple element with attributes.
//    - Map keys that begin with a hyphen, '-', are interpreted as attributes.
//      It is an error if the attribute doesn't have a []byte, string, number, or boolean value.
//    - Map value type encoding:
//          > string, bool, float64, int, int32, int64, float32: per "%v" formating
//          > []bool, []uint8: by casting to string
//          > structures, etc.: handed to xml.Marshal() - if there is an error, the element
//            value is "UNKNOWN"
//    - Elements with only attribute values or are null are terminated using "/>".
//    - If len(m) == 1 and no rootTag is provided, then the map key is used as the root tag.
//      Thus, `{ "key":"value" }` encodes as `<key>value</key>`.
func MapToDoc(m map[string]interface{}, rootTag ...string) (string, error) {
	var err error
	s := new(string)

	if len(m) == 1 && len(rootTag) == 0 {
		for key, value := range m {
			if _, ok := value.([]interface{}); ok {
				err = mapToDoc(s, DefaultRootTag, m)
			} else {
				err = mapToDoc(s, key, value)
			}
		}
	} else if len(rootTag) == 1 {
		err = mapToDoc(s, rootTag[0], m)
	} else {
		err = mapToDoc(s, DefaultRootTag, m)
	}
	return *s, err
}

// where the work actually happens
// returns an error if an attribute is not atomic
func mapToDoc(s *string, key string, value interface{}) error {
	var endTag bool

	if _, ok := value.([]interface{}); !ok {
		*s += `<` + key
	}
	switch value.(type) {
	case map[string]interface{}:
		vv := value.(map[string]interface{})
		lenvv := len(vv)
		// scan out attributes - keys have prepended hyphen, '-'
		var cntAttr int
		for k, v := range vv {
			if k[:1] == "-" {
				switch v.(type) {
				case string, float64, bool, int, int32, int64, float32:
					*s += ` ` + k[1:] + `="` + fmt.Sprintf("%v", v) + `"`
					cntAttr++
				case []byte:		// allow standard xml pkg []byte transform, as below
					*s += ` ` + k[1:] + `="` + fmt.Sprintf("%v",string(v.([]byte))) + `"`
					cntAttr++
				default:
					return errors.New("invalid attribute value for: " + k)
				}
			}
		}
		// only attributes?
		if cntAttr == lenvv {
			break
		}
		// simple element? Note: '#text" is an invalid XML tag.
		if v, ok := vv["#text"]; ok {
			if cntAttr+1 < lenvv {
				return errors.New("#text key occurs with other non-attribute keys")
			}
			*s += ">" + fmt.Sprintf("%v", v)
			endTag = true
			break
		}
		// close tag with possible attributes
		*s += ">"
		// something more complex
		for k, v := range vv {
			if k[:1] == "-" {
				continue
			}
			mapToDoc(s, k, v)
		}
		endTag = true
	case []interface{}:
		for _, v := range value.([]interface{}) {
			mapToDoc(s, key, v)
		}
		return nil
	case nil:
		// terminate the tag
		break
	default: // handle anything - even goofy stuff
		var tmp string
		switch value.(type) {
		case string, float64, bool, int, int32, int64, float32:
			tmp = fmt.Sprintf("%v", value)
		case []byte:			// NOTE: byte is just an alias for uint8
			// similar to how xml.Marshal handles []byte structure members
			tmp = fmt.Sprintf("%v", string(value.([]byte)))
		default:
			v, err := xml.Marshal(value)
			if err != nil {
				tmp = "UNKNOWN"
			} else {
				tmp = string(v)
			}
		}
		*s += ">" + tmp
		endTag = true
	}

	if endTag {
		*s += "</" + key + ">"
	} else {
		*s += "/>"
	}
	return nil
}
<|endoftext|>


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

package goacme

import (
	"crypto/rsa"
	"encoding/base64"
	"encoding/json"
	// TODO: replace this silly library!
	jose "github.com/letsencrypt/go-jose"
)

type jwsHeader struct {
	Alg   string `json:"alg"`
	Typ   string `json:"typ"`
	JWK   string `json:"jwk"`
	Nonce string `json:"nonce"`
}

func (h *jwsHeader) encode() (string, error) {
	b, err := json.Marshal(h)
	if err != nil {
		return "", err
	}
	return base64.RawURLEncoding.EncodeToString(b), nil
}

func jwsEncode(claims interface{}, key *rsa.PrivateKey, nonce string) (string, error) {
	body, err := json.Marshal(claims)
	if err != nil {
		return "", err
	}
	s, err := jose.NewSigner(jose.RS256, key)
	if err != nil {
		return "", err
	}
	sig, err := s.Sign(body, nonce)
	if err != nil {
		return "", err
	}
	return sig.FullSerialize(), nil
}
<commit_msg>Temp. fix for go-jose upstream changes<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//     http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package goacme

import (
	"crypto/rsa"
	"encoding/base64"
	"encoding/json"

	jose "github.com/letsencrypt/go-jose"
)

type jwsHeader struct {
	Alg   string `json:"alg"`
	Typ   string `json:"typ"`
	JWK   string `json:"jwk"`
	Nonce string `json:"nonce"`
}

func (h *jwsHeader) encode() (string, error) {
	b, err := json.Marshal(h)
	if err != nil {
		return "", err
	}
	return base64.RawURLEncoding.EncodeToString(b), nil
}

func jwsEncode(claims interface{}, key *rsa.PrivateKey, nonce string) (string, error) {
	body, err := json.Marshal(claims)
	if err != nil {
		return "", err
	}
	s, err := jose.NewSigner(jose.RS256, key)
	if err != nil {
		return "", err
	}
	s.SetNonceSource(staticNonceSource(nonce))
	sig, err := s.Sign(body)
	if err != nil {
		return "", err
	}
	return sig.FullSerialize(), nil
}

type staticNonceSource string

func (s staticNonceSource) Nonce() (string, error) {
	return string(s), nil
}
<|endoftext|>


<commit_before>package rgo

import(
    "math"
)

// Probability A float belonging to [0,1]
type Probability float64

// Law Implements a probalistic law
type Law interface{
    // Generate
    Rn(n int) []interface{}
    R() interface{}
    // probability density function
    D(value interface{}) Probability
    // cumulative probability density function
    P(value interface{}) Probability
    // quantile
    Q(value Probability) float64
}

// Discrete Implements a discrete law
type Discrete interface{
    Rn(length int) []int
    R() int
    D(value int) Probability
    P(value int) Probability
}

// Continuous Implements a continous law
type Continuous interface{
    Rn(length float64) []int
    R() int
    D(value float64) Probability
    P(value float64) Probability
}
<commit_msg>Changed Law interface to match Golang standards<commit_after>package rgo

import(
    "math"
)

// Probability A float belonging to [0,1]
type Probability float64

// Lawer Implements a probalistic law
type Lawer interface{
    // Generate
    Rn(n int) []interface{}
    R() interface{}
    // probability density function
    D(value interface{}) Probability
    // cumulative probability density function
    P(value interface{}) Probability
    // quantile
    Q(value Probability) float64
}

// Discrete Implements a discrete law
type Discrete interface{
    Rn(length int) []int
    R() int
    D(value int) Probability
    P(value int) Probability
}

// Continuous Implements a continous law
type Continuous interface{
    Rn(length float64) []int
    R() int
    D(value float64) Probability
    P(value float64) Probability
}
<|endoftext|>


<commit_before>package log

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
)

var trace = log.New(ioutil.Discard, "T: ", log.Ldate|log.Ltime|log.Lshortfile)
var info = log.New(os.Stdout, "I: ", log.Ldate|log.Ltime|log.Lshortfile)
var warning = log.New(os.Stdout, "W: ", log.Ldate|log.Ltime|log.Lshortfile)
var error = log.New(os.Stderr, "E: ", log.Ldate|log.Ltime|log.Lshortfile)
var slack = log.New(os.Stdout, "SLACK: ", log.Ldate|log.Ltime|log.Lshortfile)
var request = log.New(os.Stdout, "R: ", log.Ldate|log.Ltime)

var callDepth int = 2

func Errorf(format string, v ...interface{}) {
	error.Output(callDepth, f(format, v...))
}

func Errorln(v ...interface{}) {
	error.Output(callDepth, ln(v...))
}

func Warningf(format string, v ...interface{}) {
	warning.Output(callDepth, f(format, v...))
}

func Warningln(v ...interface{}) {
	warning.Output(callDepth, ln(v...))
}

func Infof(format string, v ...interface{}) {
	info.Output(callDepth, f(format, v...))
}

func Infoln(v ...interface{}) {
	info.Output(callDepth, ln(v...))
}

func Requestln(v ...interface{}) {
	request.Println(v...)
}

func Tracef(format string, v ...interface{}) {
	trace.Output(callDepth, f(format, v...))
}

func Traceln(v ...interface{}) {
	trace.Output(callDepth, ln(v...))
}

func Panicln(v ...interface{}) {
	string := ln(v...)
	trace.Output(callDepth, string)
	panic(string)
}

func Panicf(format string, v ...interface{}) {
	string := f(format, v...)
	trace.Output(callDepth, string)
	panic(string)
}

func Slackf(format string, v ...interface{}) {
	slack.Output(callDepth, f(format, v...))
}

func SlackLn(v ...interface{}) {
	slack.Output(callDepth, ln(v...))
}

func f(format string, v ...interface{}) string {
	return ln(fmt.Sprintf(format, v...))
}

func ln(a ...interface{}) string {
	return fmt.Sprintln(a...)
}
<commit_msg>allow setting of prefix<commit_after>package log

import (
	"fmt"
	"log"
	"os"
)

type Logger struct {
	info    *log.Logger
	warning *log.Logger
	error   *log.Logger
	slack   *log.Logger
	request *log.Logger
}

func New(prefix string) *Logger {

	l := &Logger{
		info:    log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		warning: log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		error:   log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile),
		slack:   log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		request: log.New(os.Stdout, "", log.Ldate|log.Ltime),
	}

	l.SetPrefix(prefix)
	return l
}

var DefaultLogger = New("")

type BLogger interface {
	Errorf(format string, v ...interface{})
	Errorln(v ...interface{})
	Warningf(format string, v ...interface{})
	Warningln(v ...interface{})
	Infof(format string, v ...interface{})
	Infoln(v ...interface{})
	Requestln(v ...interface{})
	Panicln(v ...interface{})
	Panicf(format string, v ...interface{})
	Slackf(format string, v ...interface{})
	SlackLn(v ...interface{})
}

var callDepth int = 2

func (this *Logger) SetPrefix(prefix string) {

	if prefix != "" {
		prefix = prefix + " "
	}

	this.info.SetPrefix(prefix + "I: ")
	this.warning.SetPrefix(prefix + "W: ")
	this.error.SetPrefix(prefix + "E: ")
	this.slack.SetPrefix(prefix + "SLACK: ")
	this.request.SetPrefix(prefix + "R: ")
}

func (this *Logger) Errorf(format string, v ...interface{}) {
	this.error.Output(callDepth, f(format, v...))
}

func (this *Logger) Errorln(v ...interface{}) {
	this.error.Output(callDepth, ln(v...))
}

func (this *Logger) Warningf(format string, v ...interface{}) {
	this.warning.Output(callDepth, f(format, v...))
}

func (this *Logger) Warningln(v ...interface{}) {
	this.warning.Output(callDepth, ln(v...))
}

func (this *Logger) Infof(format string, v ...interface{}) {
	this.info.Output(callDepth, f(format, v...))
}

func (this *Logger) Infoln(v ...interface{}) {
	this.info.Output(callDepth, ln(v...))
}

func (this *Logger) Requestln(v ...interface{}) {
	this.request.Println(v...)
}

func (this *Logger) Panicln(v ...interface{}) {
	string := ln(v...)
	this.error.Output(callDepth, string)
	panic(string)
}

func (this *Logger) Panicf(format string, v ...interface{}) {
	string := f(format, v...)
	this.error.Output(callDepth, string)
	panic(string)
}

func (this *Logger) Slackf(format string, v ...interface{}) {
	this.slack.Output(callDepth, f(format, v...))
}

func (this *Logger) SlackLn(v ...interface{}) {
	this.slack.Output(callDepth, ln(v...))
}

//Globals
func Errorf(format string, v ...interface{}) {
	DefaultLogger.Errorf(format, v...)
}

func Errorln(v ...interface{}) {
	DefaultLogger.Errorln(v...)
}

func Warningf(format string, v ...interface{}) {
	DefaultLogger.Warningf(format, v...)
}

func Warningln(v ...interface{}) {
	DefaultLogger.Warningln(v...)
}

func Infof(format string, v ...interface{}) {
	DefaultLogger.Infof(format, v...)
}

func Infoln(v ...interface{}) {
	DefaultLogger.Infoln(v...)
}

func Requestln(v ...interface{}) {
	DefaultLogger.Requestln(v...)
}

func Panicln(v ...interface{}) {
	DefaultLogger.Panicln(v...)
}

func Panicf(format string, v ...interface{}) {
	DefaultLogger.Panicf(format, v...)
}

func Slackf(format string, v ...interface{}) {
	DefaultLogger.Slackf(format, v...)
}

func SlackLn(v ...interface{}) {
	DefaultLogger.SlackLn(v...)
}

func f(format string, v ...interface{}) string {
	return ln(fmt.Sprintf(format, v...))
}

func ln(a ...interface{}) string {
	return fmt.Sprintln(a...)
}
<|endoftext|>


<commit_before>package elicit

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type log struct {
	ctx       *Context
	buffer    bytes.Buffer
	useColour bool
	outpath   string
}

func (l *log) writeToConsole() {
	l.useColour = true
	l.fillBuffer(false)
	fmt.Println(l.buffer.String())
}

func (l *log) writeToFile() {
	if l.outpath == "" {
		return
	}

	if err := os.MkdirAll(filepath.Dir(l.outpath), 0755); err != nil {
		panic(err)
	}

	l.useColour = false
	l.fillBuffer(true)

	if err := ioutil.WriteFile(l.outpath, bytes.TrimSpace(l.buffer.Bytes()), 0755); err != nil {
		panic(err)
	}
}

func (l *log) fillBuffer(forceVerbose bool) {
	l.buffer.Truncate(0)

	verbose := forceVerbose
	if v := flag.Lookup("test.v"); !forceVerbose && v != nil {
		verbose = v.Value.String() == "true"
	}

	for _, spec := range l.ctx.specs {
		if !verbose && !spec.result.shouldLog() {
			continue
		}
		l.writeSpecHeader(spec)

		for _, scenario := range spec.scenarios {
			if !verbose && !scenario.result.shouldLog() {
				continue
			}
			l.writeScenarioHeader(scenario)

			for _, step := range scenario.steps {
				l.writeStepResult(step)
			}

		}
	}
}

func (l *log) writeLn() {
	fmt.Fprintln(&l.buffer)
}

func (l *log) writeSpecHeader(s *spec) {
	name := s.name
	underline := strings.Repeat("=", len(s.name))
	resultCounts := [numResultTypes]int{}

	switch s.result {
	case pending:
		name = l.yellow(name)
		underline = l.yellow(underline)
	case skipped:
		name = l.blue(name)
		underline = l.blue(underline)
	case failed, panicked:
		name = l.red(name)
		underline = l.red(underline)
	}

	for _, scenario := range s.scenarios {
		resultCounts[scenario.result]++
	}

	resultString := ""

	for i, count := range resultCounts {
		if count > 0 {
			resultString += fmt.Sprintf("\n%s: %d", result(i), count)
		}
	}

	fmt.Fprintf(&l.buffer, "\n\n%s\n%s%s\n", name, underline, resultString)
}

func (l *log) writeScenarioHeader(s *scenario) {
	name := s.name
	underline := strings.Repeat("-", len(s.name))

	switch s.result {
	case pending:
		name = l.yellow(name)
		underline = l.yellow(underline)
	case skipped:
		name = l.blue(name)
		underline = l.blue(underline)
	case failed, panicked:
		name = l.red(name)
		underline = l.red(underline)
	}

	fmt.Fprintf(&l.buffer, "\n%s\n%s\n%s\n\n", name, underline, s.result)
}

func (l *log) writeStepResult(s *step) {
	var prefix, suffix string
	text := s.text

	textBlocks := len(s.textBlocks)
	if textBlocks > 0 {
		suffix += strings.Repeat(" ☰", textBlocks)
	}

	tables := len(s.tables)
	if tables > 0 {
		suffix += strings.Repeat(" ☷", tables)
	}

	switch s.result {
	case pending:
		prefix = l.yellow("?")
		text = l.yellow(text)
	case skipped:
		prefix = l.blue("⤹")
		text = l.blue(text)
	case failed:
		prefix = l.red("✘")
		text = l.red(text)
	case panicked:
		prefix = l.red("⚡")
		text = l.red(text)
	case passed:
		prefix = l.green("✓")
	}

	fmt.Fprintf(&l.buffer, "    %s %s%s\n", prefix, text, suffix)

	if s.log.Len() > 0 {
		leftPad := "        "
		stepLog := s.log.String()
		stepLog = strings.TrimSuffix(stepLog, "\n")
		lines := strings.Split(stepLog, "\n")
		stepLog = leftPad + strings.Join(lines, "\n"+leftPad)
		fmt.Fprintln(&l.buffer, stepLog)
	}
}

func (l *log) red(s string) string {
	return l.colour(s, 31)
}

func (l *log) green(s string) string {
	return l.colour(s, 32)
}

func (l *log) yellow(s string) string {
	return l.colour(s, 33)
}

func (l *log) blue(s string) string {
	return l.colour(s, 34)
}

func (l *log) colour(s string, colour int) string {
	if l.useColour {
		s = fmt.Sprintf("\033[%dm%s\033[0m", colour, s)
	}
	return s
}
<commit_msg>Stop Printing Empty Line When There's No Output<commit_after>package elicit

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type log struct {
	ctx       *Context
	buffer    bytes.Buffer
	useColour bool
	outpath   string
}

func (l *log) writeToConsole() {
	l.useColour = true
	l.fillBuffer(false)
	if l.buffer.Len() > 0 {
		fmt.Println(l.buffer.String())
	}
}

func (l *log) writeToFile() {
	if l.outpath == "" {
		return
	}

	if err := os.MkdirAll(filepath.Dir(l.outpath), 0755); err != nil {
		panic(err)
	}

	l.useColour = false
	l.fillBuffer(true)

	if err := ioutil.WriteFile(l.outpath, bytes.TrimSpace(l.buffer.Bytes()), 0755); err != nil {
		panic(err)
	}
}

func (l *log) fillBuffer(forceVerbose bool) {
	l.buffer.Truncate(0)

	verbose := forceVerbose
	if v := flag.Lookup("test.v"); !forceVerbose && v != nil {
		verbose = v.Value.String() == "true"
	}

	for _, spec := range l.ctx.specs {
		if !verbose && !spec.result.shouldLog() {
			continue
		}
		l.writeSpecHeader(spec)

		for _, scenario := range spec.scenarios {
			if !verbose && !scenario.result.shouldLog() {
				continue
			}
			l.writeScenarioHeader(scenario)

			for _, step := range scenario.steps {
				l.writeStepResult(step)
			}

		}
	}
}

func (l *log) writeLn() {
	fmt.Fprintln(&l.buffer)
}

func (l *log) writeSpecHeader(s *spec) {
	name := s.name
	underline := strings.Repeat("=", len(s.name))
	resultCounts := [numResultTypes]int{}

	switch s.result {
	case pending:
		name = l.yellow(name)
		underline = l.yellow(underline)
	case skipped:
		name = l.blue(name)
		underline = l.blue(underline)
	case failed, panicked:
		name = l.red(name)
		underline = l.red(underline)
	}

	for _, scenario := range s.scenarios {
		resultCounts[scenario.result]++
	}

	resultString := ""

	for i, count := range resultCounts {
		if count > 0 {
			resultString += fmt.Sprintf("\n%s: %d", result(i), count)
		}
	}

	fmt.Fprintf(&l.buffer, "\n\n%s\n%s%s\n", name, underline, resultString)
}

func (l *log) writeScenarioHeader(s *scenario) {
	name := s.name
	underline := strings.Repeat("-", len(s.name))

	switch s.result {
	case pending:
		name = l.yellow(name)
		underline = l.yellow(underline)
	case skipped:
		name = l.blue(name)
		underline = l.blue(underline)
	case failed, panicked:
		name = l.red(name)
		underline = l.red(underline)
	}

	fmt.Fprintf(&l.buffer, "\n%s\n%s\n%s\n\n", name, underline, s.result)
}

func (l *log) writeStepResult(s *step) {
	var prefix, suffix string
	text := s.text

	textBlocks := len(s.textBlocks)
	if textBlocks > 0 {
		suffix += strings.Repeat(" ☰", textBlocks)
	}

	tables := len(s.tables)
	if tables > 0 {
		suffix += strings.Repeat(" ☷", tables)
	}

	switch s.result {
	case pending:
		prefix = l.yellow("?")
		text = l.yellow(text)
	case skipped:
		prefix = l.blue("⤹")
		text = l.blue(text)
	case failed:
		prefix = l.red("✘")
		text = l.red(text)
	case panicked:
		prefix = l.red("⚡")
		text = l.red(text)
	case passed:
		prefix = l.green("✓")
	}

	fmt.Fprintf(&l.buffer, "    %s %s%s\n", prefix, text, suffix)

	if s.log.Len() > 0 {
		leftPad := "        "
		stepLog := s.log.String()
		stepLog = strings.TrimSuffix(stepLog, "\n")
		lines := strings.Split(stepLog, "\n")
		stepLog = leftPad + strings.Join(lines, "\n"+leftPad)
		fmt.Fprintln(&l.buffer, stepLog)
	}
}

func (l *log) red(s string) string {
	return l.colour(s, 31)
}

func (l *log) green(s string) string {
	return l.colour(s, 32)
}

func (l *log) yellow(s string) string {
	return l.colour(s, 33)
}

func (l *log) blue(s string) string {
	return l.colour(s, 34)
}

func (l *log) colour(s string, colour int) string {
	if l.useColour {
		s = fmt.Sprintf("\033[%dm%s\033[0m", colour, s)
	}
	return s
}
<|endoftext|>


<commit_before>package gou

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"strings"
	"sync"
	"time"
)

const (
	NOLOGGING = -1
	FATAL     = 0
	ERROR     = 1
	WARN      = 2
	INFO      = 3
	DEBUG     = 4
)

/*
https://github.com/mewkiz/pkg/tree/master/term
RED = '\033[0;1;31m'
GREEN = '\033[0;1;32m'
YELLOW = '\033[0;1;33m'
BLUE = '\033[0;1;34m'
MAGENTA = '\033[0;1;35m'
CYAN = '\033[0;1;36m'
WHITE = '\033[0;1;37m'
DARK_MAGENTA = '\033[0;35m'
ANSI_RESET = '\033[0m'
LogColor         = map[int]string{FATAL: "\033[0m\033[37m",
	ERROR: "\033[0m\033[31m",
	WARN:  "\033[0m\033[33m",
	INFO:  "\033[0m\033[32m",
	DEBUG: "\033[0m\033[34m"}

\e]PFdedede
*/

var (
	LogLevel    int = ERROR
	EMPTY       struct{}
	ErrLogLevel int = ERROR
	logger      *log.Logger
	loggerErr   *log.Logger
	LogColor    = map[int]string{FATAL: "\033[0m\033[37m",
		ERROR: "\033[0m\033[31m",
		WARN:  "\033[0m\033[33m",
		INFO:  "\033[0m\033[35m",
		DEBUG: "\033[0m\033[34m"}
	LogPrefix = map[int]string{
		FATAL: "[FATAL] ",
		ERROR: "[ERROR] ",
		WARN:  "[WARN] ",
		INFO:  "[INFO] ",
		DEBUG: "[DEBUG] ",
	}
	postFix                      = "" //\033[0m
	LogLevelWords map[string]int = map[string]int{"fatal": 0, "error": 1, "warn": 2, "info": 3, "debug": 4, "none": -1}
	logThrottles                 = make(map[string]*Throttler)
	throttleMu    sync.Mutex
)

// Setup default logging to Stderr, equivalent to:
//
//	gou.SetLogger(log.New(os.Stderr, "", log.Ltime|log.Lshortfile), "debug")
func SetupLogging(lvl string) {
	SetLogger(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds), strings.ToLower(lvl))
}

// Setup default logging to Stderr, equivalent to:
//
//	gou.SetLogger(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds), level)
func SetupLoggingLong(lvl string) {
	SetLogger(log.New(os.Stderr, "", log.LstdFlags|log.Llongfile|log.Lmicroseconds), strings.ToLower(lvl))
}

// Setup colorized output if this is a terminal
func SetColorIfTerminal() {
	if IsTerminal() {
		SetColorOutput()
	}
}

// Setup colorized output
func SetColorOutput() {
	for lvl, color := range LogColor {
		LogPrefix[lvl] = color
	}
	postFix = "\033[0m"
}

// Setup default log output to go to a dev/null
//
//	log.SetOutput(new(DevNull))
func DiscardStandardLogger() {
	log.SetOutput(new(DevNull))
}

// you can set a logger, and log level,most common usage is:
//
//	gou.SetLogger(log.New(os.Stdout, "", log.LstdFlags), "debug")
//
//  loglevls:   debug, info, warn, error, fatal
// Note, that you can also set a seperate Error Log Level
func SetLogger(l *log.Logger, logLevel string) {
	logger = l
	LogLevelSet(logLevel)
}
func GetLogger() *log.Logger {
	return logger
}

// you can set a logger, and log level.  this is for errors, and assumes
// you are logging to Stderr (seperate from stdout above), allowing you to seperate
// debug&info logging from errors
//
//	gou.SetLogger(log.New(os.Stderr, "", log.LstdFlags), "debug")
//
//  loglevls:   debug, info, warn, error, fatal
func SetErrLogger(l *log.Logger, logLevel string) {
	loggerErr = l
	if lvl, ok := LogLevelWords[logLevel]; ok {
		ErrLogLevel = lvl
	}
}
func GetErrLogger() *log.Logger {
	return logger
}

// sets the log level from a string
func LogLevelSet(levelWord string) {
	if lvl, ok := LogLevelWords[levelWord]; ok {
		LogLevel = lvl
	}
}

// Log at debug level
func Debug(v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, DEBUG, fmt.Sprint(v...))
	}
}

// Debug log formatted
func Debugf(format string, v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, DEBUG, fmt.Sprintf(format, v...))
	}
}

// Log at info level
func Info(v ...interface{}) {
	if LogLevel >= 3 {
		DoLog(3, INFO, fmt.Sprint(v...))
	}
}

// info log formatted
func Infof(format string, v ...interface{}) {
	if LogLevel >= 3 {
		DoLog(3, INFO, fmt.Sprintf(format, v...))
	}
}

// Log at warn level
func Warn(v ...interface{}) {
	if LogLevel >= 2 {
		DoLog(3, WARN, fmt.Sprint(v...))
	}
}

// Debug log formatted
func Warnf(format string, v ...interface{}) {
	if LogLevel >= 2 {
		DoLog(3, WARN, fmt.Sprintf(format, v...))
	}
}

// Log at error level
func Error(v ...interface{}) {
	if LogLevel >= 1 {
		DoLog(3, ERROR, fmt.Sprint(v...))
	}
}

// Error log formatted
func Errorf(format string, v ...interface{}) {
	if LogLevel >= 1 {
		DoLog(3, ERROR, fmt.Sprintf(format, v...))
	}
}

// Log this error, and return error object
func LogErrorf(format string, v ...interface{}) error {
	err := fmt.Errorf(format, v...)
	if LogLevel >= 1 {
		DoLog(3, ERROR, err.Error())
	}
	return err
}

// Log to logger if setup
//    Log(ERROR, "message")
func Log(logLvl int, v ...interface{}) {
	if LogLevel >= logLvl {
		DoLog(3, logLvl, fmt.Sprint(v...))
	}
}

// Log to logger if setup, grab a stack trace and add that as well
//
//    u.LogTracef(u.ERROR, "message %s", varx)
//
func LogTracef(logLvl int, format string, v ...interface{}) {
	if LogLevel >= logLvl {
		// grab a stack trace
		stackBuf := make([]byte, 6000)
		stackBufLen := runtime.Stack(stackBuf, false)
		stackTraceStr := string(stackBuf[0:stackBufLen])
		parts := strings.Split(stackTraceStr, "\n")
		if len(parts) > 1 {
			v = append(v, strings.Join(parts[3:len(parts)], "\n"))
		}
		DoLog(3, logLvl, fmt.Sprintf(format+"\n%v", v...))
	}
}

// Throttle logging based on key, such that key would never occur more than
//   @limit times per hour
//
//    LogThrottleKey(u.ERROR, 1,"error_that_happens_a_lot" "message %s", varx)
//
func LogThrottleKey(logLvl, limit int, key, format string, v ...interface{}) {
	if LogLevel >= logLvl {
		throttleMu.Lock()
		th, ok := logThrottles[key]
		if !ok {
			th = NewThrottler(limit, 3600*time.Second)
			logThrottles[key] = th
		}
		if th.Throttle() {
			throttleMu.Unlock()
			return
		}
		throttleMu.Unlock()
		DoLog(3, logLvl, fmt.Sprintf(format, v...))
	}
}

// Throttle logging based on @format as a key, such that key would never occur more than
//   @limit times per hour
//
//    LogThrottle(u.ERROR, 1, "message %s", varx)
//
func LogThrottle(logLvl, limit int, format string, v ...interface{}) {
	if LogLevel >= logLvl {
		throttleMu.Lock()
		th, ok := logThrottles[format]
		if !ok {
			th = NewThrottler(limit, 3600*time.Second)
			logThrottles[format] = th
		}
		if th.Throttle() {
			throttleMu.Unlock()
			return
		}
		throttleMu.Unlock()
		DoLog(3, logLvl, fmt.Sprintf(format, v...))
	}
}

// Log to logger if setup
//    Logf(ERROR, "message %d", 20)
func Logf(logLvl int, format string, v ...interface{}) {
	if LogLevel >= logLvl {
		DoLog(3, logLvl, fmt.Sprintf(format, v...))
	}
}

// Log to logger if setup
//    LogP(ERROR, "prefix", "message", anyItems, youWant)
func LogP(logLvl int, prefix string, v ...interface{}) {
	if ErrLogLevel >= logLvl && loggerErr != nil {
		loggerErr.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprint(v...)+postFix)
	} else if LogLevel >= logLvl && logger != nil {
		logger.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprint(v...)+postFix)
	}
}

// Log to logger if setup with a prefix
//    LogPf(ERROR, "prefix", "formatString %s %v", anyItems, youWant)
func LogPf(logLvl int, prefix string, format string, v ...interface{}) {
	if ErrLogLevel >= logLvl && loggerErr != nil {
		loggerErr.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprintf(format, v...)+postFix)
	} else if LogLevel >= logLvl && logger != nil {
		logger.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprintf(format, v...)+postFix)
	}
}

// When you want to use the log short filename flag, and want to use
// the lower level logging functions (say from an *Assert* type function)
// you need to modify the stack depth:
//
//     func init() {}
// 	       SetLogger(log.New(os.Stderr, "", log.Ltime|log.Lshortfile|log.Lmicroseconds), lvl)
//     }
//
//     func assert(t *testing.T, myData) {
//         // we want log line to show line that called this assert, not this line
//         LogD(5, DEBUG, v...)
//     }
func LogD(depth int, logLvl int, v ...interface{}) {
	if LogLevel >= logLvl {
		DoLog(depth, logLvl, fmt.Sprint(v...))
	}
}

// Low level log with depth , level, message and logger
func DoLog(depth, logLvl int, msg string) {
	if ErrLogLevel >= logLvl && loggerErr != nil {
		loggerErr.Output(depth, LogPrefix[logLvl]+msg+postFix)
	} else if LogLevel >= logLvl && logger != nil {
		logger.Output(depth, LogPrefix[logLvl]+msg+postFix)
	}
}

type winsize struct {
	Row    uint16
	Col    uint16
	Xpixel uint16
	Ypixel uint16
}

const (
	_TIOCGWINSZ = 0x5413 // OSX 1074295912
)

//http://play.golang.org/p/5LIA41Iqfp
// Dummy discard, satisfies io.Writer without importing io or os.
type DevNull struct{}

func (DevNull) Write(p []byte) (int, error) {
	return len(p), nil
}
<commit_msg>depth on logthrottle<commit_after>package gou

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"strings"
	"sync"
	"time"
)

const (
	NOLOGGING = -1
	FATAL     = 0
	ERROR     = 1
	WARN      = 2
	INFO      = 3
	DEBUG     = 4
)

/*
https://github.com/mewkiz/pkg/tree/master/term
RED = '\033[0;1;31m'
GREEN = '\033[0;1;32m'
YELLOW = '\033[0;1;33m'
BLUE = '\033[0;1;34m'
MAGENTA = '\033[0;1;35m'
CYAN = '\033[0;1;36m'
WHITE = '\033[0;1;37m'
DARK_MAGENTA = '\033[0;35m'
ANSI_RESET = '\033[0m'
LogColor         = map[int]string{FATAL: "\033[0m\033[37m",
	ERROR: "\033[0m\033[31m",
	WARN:  "\033[0m\033[33m",
	INFO:  "\033[0m\033[32m",
	DEBUG: "\033[0m\033[34m"}

\e]PFdedede
*/

var (
	LogLevel    int = ERROR
	EMPTY       struct{}
	ErrLogLevel int = ERROR
	logger      *log.Logger
	loggerErr   *log.Logger
	LogColor    = map[int]string{FATAL: "\033[0m\033[37m",
		ERROR: "\033[0m\033[31m",
		WARN:  "\033[0m\033[33m",
		INFO:  "\033[0m\033[35m",
		DEBUG: "\033[0m\033[34m"}
	LogPrefix = map[int]string{
		FATAL: "[FATAL] ",
		ERROR: "[ERROR] ",
		WARN:  "[WARN] ",
		INFO:  "[INFO] ",
		DEBUG: "[DEBUG] ",
	}
	postFix                      = "" //\033[0m
	LogLevelWords map[string]int = map[string]int{"fatal": 0, "error": 1, "warn": 2, "info": 3, "debug": 4, "none": -1}
	logThrottles                 = make(map[string]*Throttler)
	throttleMu    sync.Mutex
)

// Setup default logging to Stderr, equivalent to:
//
//	gou.SetLogger(log.New(os.Stderr, "", log.Ltime|log.Lshortfile), "debug")
func SetupLogging(lvl string) {
	SetLogger(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds), strings.ToLower(lvl))
}

// Setup default logging to Stderr, equivalent to:
//
//	gou.SetLogger(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds), level)
func SetupLoggingLong(lvl string) {
	SetLogger(log.New(os.Stderr, "", log.LstdFlags|log.Llongfile|log.Lmicroseconds), strings.ToLower(lvl))
}

// Setup colorized output if this is a terminal
func SetColorIfTerminal() {
	if IsTerminal() {
		SetColorOutput()
	}
}

// Setup colorized output
func SetColorOutput() {
	for lvl, color := range LogColor {
		LogPrefix[lvl] = color
	}
	postFix = "\033[0m"
}

// Setup default log output to go to a dev/null
//
//	log.SetOutput(new(DevNull))
func DiscardStandardLogger() {
	log.SetOutput(new(DevNull))
}

// you can set a logger, and log level,most common usage is:
//
//	gou.SetLogger(log.New(os.Stdout, "", log.LstdFlags), "debug")
//
//  loglevls:   debug, info, warn, error, fatal
// Note, that you can also set a seperate Error Log Level
func SetLogger(l *log.Logger, logLevel string) {
	logger = l
	LogLevelSet(logLevel)
}
func GetLogger() *log.Logger {
	return logger
}

// you can set a logger, and log level.  this is for errors, and assumes
// you are logging to Stderr (seperate from stdout above), allowing you to seperate
// debug&info logging from errors
//
//	gou.SetLogger(log.New(os.Stderr, "", log.LstdFlags), "debug")
//
//  loglevls:   debug, info, warn, error, fatal
func SetErrLogger(l *log.Logger, logLevel string) {
	loggerErr = l
	if lvl, ok := LogLevelWords[logLevel]; ok {
		ErrLogLevel = lvl
	}
}
func GetErrLogger() *log.Logger {
	return logger
}

// sets the log level from a string
func LogLevelSet(levelWord string) {
	if lvl, ok := LogLevelWords[levelWord]; ok {
		LogLevel = lvl
	}
}

// Log at debug level
func Debug(v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, DEBUG, fmt.Sprint(v...))
	}
}

// Debug log formatted
func Debugf(format string, v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, DEBUG, fmt.Sprintf(format, v...))
	}
}

// Log at info level
func Info(v ...interface{}) {
	if LogLevel >= 3 {
		DoLog(3, INFO, fmt.Sprint(v...))
	}
}

// info log formatted
func Infof(format string, v ...interface{}) {
	if LogLevel >= 3 {
		DoLog(3, INFO, fmt.Sprintf(format, v...))
	}
}

// Log at warn level
func Warn(v ...interface{}) {
	if LogLevel >= 2 {
		DoLog(3, WARN, fmt.Sprint(v...))
	}
}

// Debug log formatted
func Warnf(format string, v ...interface{}) {
	if LogLevel >= 2 {
		DoLog(3, WARN, fmt.Sprintf(format, v...))
	}
}

// Log at error level
func Error(v ...interface{}) {
	if LogLevel >= 1 {
		DoLog(3, ERROR, fmt.Sprint(v...))
	}
}

// Error log formatted
func Errorf(format string, v ...interface{}) {
	if LogLevel >= 1 {
		DoLog(3, ERROR, fmt.Sprintf(format, v...))
	}
}

// Log this error, and return error object
func LogErrorf(format string, v ...interface{}) error {
	err := fmt.Errorf(format, v...)
	if LogLevel >= 1 {
		DoLog(3, ERROR, err.Error())
	}
	return err
}

// Log to logger if setup
//    Log(ERROR, "message")
func Log(logLvl int, v ...interface{}) {
	if LogLevel >= logLvl {
		DoLog(3, logLvl, fmt.Sprint(v...))
	}
}

// Log to logger if setup, grab a stack trace and add that as well
//
//    u.LogTracef(u.ERROR, "message %s", varx)
//
func LogTracef(logLvl int, format string, v ...interface{}) {
	if LogLevel >= logLvl {
		// grab a stack trace
		stackBuf := make([]byte, 6000)
		stackBufLen := runtime.Stack(stackBuf, false)
		stackTraceStr := string(stackBuf[0:stackBufLen])
		parts := strings.Split(stackTraceStr, "\n")
		if len(parts) > 1 {
			v = append(v, strings.Join(parts[3:len(parts)], "\n"))
		}
		DoLog(3, logLvl, fmt.Sprintf(format+"\n%v", v...))
	}
}

// Throttle logging based on key, such that key would never occur more than
//   @limit times per hour
//
//    LogThrottleKey(u.ERROR, 1,"error_that_happens_a_lot" "message %s", varx)
//
func LogThrottleKey(logLvl, limit int, key, format string, v ...interface{}) {
	if LogLevel >= logLvl {
		throttleMu.Lock()
		th, ok := logThrottles[key]
		if !ok {
			th = NewThrottler(limit, 3600*time.Second)
			logThrottles[key] = th
		}
		if th.Throttle() {
			throttleMu.Unlock()
			return
		}
		throttleMu.Unlock()
		DoLog(3, logLvl, fmt.Sprintf(format, v...))
	}
}

// Throttle logging based on @format as a key, such that key would never occur more than
//   @limit times per hour
//
//    LogThrottle(u.ERROR, 1, "message %s", varx)
//
func LogThrottle(logLvl, limit int, format string, v ...interface{}) {
	if LogLevel >= logLvl {
		throttleMu.Lock()
		th, ok := logThrottles[format]
		if !ok {
			th = NewThrottler(limit, 3600*time.Second)
			logThrottles[format] = th
		}
		if th.Throttle() {
			throttleMu.Unlock()
			return
		}
		throttleMu.Unlock()
		DoLog(3, logLvl, fmt.Sprintf(format, v...))
	}
}

// Throttle logging based on @format as a key, such that key would never occur more than
//   @limit times per hour
//
//    LogThrottleD(5, u.ERROR, 1, "message %s", varx)
//
func LogThrottleD(depth, logLvl, limit int, format string, v ...interface{}) {
	if LogLevel >= logLvl {
		throttleMu.Lock()
		th, ok := logThrottles[format]
		if !ok {
			th = NewThrottler(limit, 3600*time.Second)
			logThrottles[format] = th
		}
		if th.Throttle() {
			throttleMu.Unlock()
			return
		}
		throttleMu.Unlock()
		DoLog(depth, logLvl, fmt.Sprintf(format, v...))
	}
}

// Log to logger if setup
//    Logf(ERROR, "message %d", 20)
func Logf(logLvl int, format string, v ...interface{}) {
	if LogLevel >= logLvl {
		DoLog(3, logLvl, fmt.Sprintf(format, v...))
	}
}

// Log to logger if setup
//    LogP(ERROR, "prefix", "message", anyItems, youWant)
func LogP(logLvl int, prefix string, v ...interface{}) {
	if ErrLogLevel >= logLvl && loggerErr != nil {
		loggerErr.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprint(v...)+postFix)
	} else if LogLevel >= logLvl && logger != nil {
		logger.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprint(v...)+postFix)
	}
}

// Log to logger if setup with a prefix
//    LogPf(ERROR, "prefix", "formatString %s %v", anyItems, youWant)
func LogPf(logLvl int, prefix string, format string, v ...interface{}) {
	if ErrLogLevel >= logLvl && loggerErr != nil {
		loggerErr.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprintf(format, v...)+postFix)
	} else if LogLevel >= logLvl && logger != nil {
		logger.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprintf(format, v...)+postFix)
	}
}

// When you want to use the log short filename flag, and want to use
// the lower level logging functions (say from an *Assert* type function)
// you need to modify the stack depth:
//
//     func init() {}
// 	       SetLogger(log.New(os.Stderr, "", log.Ltime|log.Lshortfile|log.Lmicroseconds), lvl)
//     }
//
//     func assert(t *testing.T, myData) {
//         // we want log line to show line that called this assert, not this line
//         LogD(5, DEBUG, v...)
//     }
func LogD(depth int, logLvl int, v ...interface{}) {
	if LogLevel >= logLvl {
		DoLog(depth, logLvl, fmt.Sprint(v...))
	}
}

// Low level log with depth , level, message and logger
func DoLog(depth, logLvl int, msg string) {
	if ErrLogLevel >= logLvl && loggerErr != nil {
		loggerErr.Output(depth, LogPrefix[logLvl]+msg+postFix)
	} else if LogLevel >= logLvl && logger != nil {
		logger.Output(depth, LogPrefix[logLvl]+msg+postFix)
	}
}

type winsize struct {
	Row    uint16
	Col    uint16
	Xpixel uint16
	Ypixel uint16
}

const (
	_TIOCGWINSZ = 0x5413 // OSX 1074295912
)

//http://play.golang.org/p/5LIA41Iqfp
// Dummy discard, satisfies io.Writer without importing io or os.
type DevNull struct{}

func (DevNull) Write(p []byte) (int, error) {
	return len(p), nil
}
<|endoftext|>