<commit_before>package main

import (
	"bufio"
	"encoding/binary"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"sync"

	"github.com/layeh/gopus"
)

// All global variables used within the program
var (
	// 1 for mono, 2 for stereo
	Channels int

	// Must be one of 8000, 12000, 16000, 24000, or 48000.
	// Discord only uses 48000 currently.
	FrameRate int

	// Rates from 500 to 512000 bits per second are meaningful
	// Discord only uses 8000 to 128000 and default is 64000
	Bitrate int

	// Must be one of voip, audio, or lowdelay.
	// DCA defaults to audio which is ideal for music
	// Not sure what Discord uses here, probably voip
	Application string

	FrameSize int // uint16 size of each audio frame
	MaxBytes  int // max size of opus data

	Volume int // change audio volume (256=normal)

	OpusEncoder *gopus.Encoder

	InFile string

	OutFile string = "pipe:1"
	OutBuf  []byte

	EncodeChan chan []int16
	OutputChan chan []byte

	err error

	wg sync.WaitGroup
)

// init configures and parses the command line arguments
func init() {

	flag.StringVar(&InFile, "i", "pipe:0", "infile")
	flag.IntVar(&Volume, "vol", 256, "change audio volume (256=normal)")
	flag.IntVar(&Channels, "ac", 2, "audio channels")
	flag.IntVar(&FrameRate, "ar", 48000, "audio sampling rate")
	flag.IntVar(&FrameSize, "as", 960, "audio frame size can be 960 (20ms), 1920 (40ms), or 2880 (60ms)")
	flag.IntVar(&Bitrate, "ab", 64, "audio encoding bitrate in kb/s can be 8 - 128")
	flag.StringVar(&Application, "aa", "audio", "audio application can be voip, audio, or lowdelay")

	if len(os.Args) < 2 {
		flag.Usage()
		os.Exit(1)
	}

	flag.Parse()

	MaxBytes = (FrameSize * Channels) * 2 // max size of opus data
}

// very simple program that wraps ffmpeg and outputs raw opus data frames
// with a uint16 header for each frame with the frame length in bytes
func main() {

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Basic setup and validation
	//////////////////////////////////////////////////////////////////////////

	// If only one argument provided assume it's a filename.
	if len(os.Args) == 2 {
		InFile = os.Args[1]
	}

	// If reading from a file, verify it exists.
	if InFile != "pipe:0" {

		if _, err := os.Stat(InFile); os.IsNotExist(err) {
			fmt.Println("error: infile does not exist")
			flag.Usage()
			return
		}
	}

	// If reading from pipe, make sure pipe is open
	if InFile == "pipe:0" {
		fi, err := os.Stdin.Stat()
		if err != nil {
			fmt.Println(err)
			return
		}

		if (fi.Mode() & os.ModeCharDevice) == 0 {
		} else {
			fmt.Println("error: stdin is not a pipe.")
			flag.Usage()
			return
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Create chans, buffers, and encoder for use
	//////////////////////////////////////////////////////////////////////////

	// create an opusEncoder to use
	OpusEncoder, err = gopus.NewEncoder(FrameRate, Channels, gopus.Audio)
	if err != nil {
		fmt.Println("NewEncoder Error:", err)
		return
	}

	// set opus encoding options
	//	OpusEncoder.SetVbr(true)                // bool

	if Bitrate < 1 || Bitrate > 512 {
		Bitrate = 64 // Set to Discord default
	}
	OpusEncoder.SetBitrate(Bitrate * 1000)

	switch Application {
	case "voip":
		OpusEncoder.SetApplication(gopus.Voip)
	case "audio":
		OpusEncoder.SetApplication(gopus.Audio)
	case "lowdelay":
		OpusEncoder.SetApplication(gopus.RestrictedLowDelay)
	default:
		OpusEncoder.SetApplication(gopus.Audio)
	}

	OutputChan = make(chan []byte, 10)
	EncodeChan = make(chan []int16, 10)

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Start reader and writer workers
	//////////////////////////////////////////////////////////////////////////

	wg.Add(1)
	go reader()

	wg.Add(1)
	go encoder()

	wg.Add(1)
	go writer()

	// wait for above goroutines to finish, then exit.
	wg.Wait()
}

// reader reads from the input
func reader() {

	defer func() {
		close(EncodeChan)
		wg.Done()
	}()

	// read from file
	if InFile != "pipe:0" {

		// Create a shell command "object" to run.
		ffmpeg := exec.Command("ffmpeg", "-i", InFile, "-vol", strconv.Itoa(Volume), "-f", "s16le", "-ar", strconv.Itoa(FrameRate), "-ac", strconv.Itoa(Channels), "pipe:1")
		stdout, err := ffmpeg.StdoutPipe()
		if err != nil {
			fmt.Println("StdoutPipe Error:", err)
			return
		}

		// Starts the ffmpeg command
		err = ffmpeg.Start()
		if err != nil {
			fmt.Println("RunStart Error:", err)
			return
		}

		for {

			// read data from ffmpeg stdout
			InBuf := make([]int16, FrameSize*Channels)
			err = binary.Read(stdout, binary.LittleEndian, &InBuf)
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				return
			}
			if err != nil {
				fmt.Println("error reading from ffmpeg stdout :", err)
				return
			}

			// write pcm data to the EncodeChan
			EncodeChan <- InBuf

		}
	}

	// read input from stdin pipe
	if InFile == "pipe:0" {

		// 16KB input buffer
		rbuf := bufio.NewReaderSize(os.Stdin, 16384)
		for {

			// read data from stdin
			InBuf := make([]int16, FrameSize*Channels)

			err = binary.Read(rbuf, binary.LittleEndian, &InBuf)
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				return
			}
			if err != nil {
				fmt.Println("error reading from ffmpeg stdout :", err)
				return
			}

			// write pcm data to the EncodeChan
			EncodeChan <- InBuf
		}
	}

}

// encoder listens on the EncodeChan and encodes provided PCM16 data
// to opus, then sends the encoded data to the OutputChan
func encoder() {

	defer func() {
		close(OutputChan)
		wg.Done()
	}()

	for {
		pcm, ok := <-EncodeChan
		if !ok {
			// if chan closed, exit
			return
		}

		// try encoding pcm frame with Opus
		opus, err := OpusEncoder.Encode(pcm, FrameSize, MaxBytes)
		if err != nil {
			fmt.Println("Encoding Error:", err)
			return
		}

		// write opus data to OutputChan
		OutputChan <- opus
	}
}

// writer listens on the OutputChan and writes the output to stdout pipe
// TODO: Add support for writing directly to a file
func writer() {

	defer wg.Done()

	var opuslen uint16
	// 16KB output buffer
	wbuf := bufio.NewWriterSize(os.Stdout, 16384)

	for {
		opus, ok := <-OutputChan
		if !ok {
			// if chan closed, exit
			return
		}

		// write header
		opuslen = uint16(len(opus))
		err = binary.Write(wbuf, binary.LittleEndian, &opuslen)
		if err != nil {
			fmt.Println("error writing output: ", err)
			return
		}

		// write opus data to stdout
		err = binary.Write(wbuf, binary.LittleEndian, &opus)
		if err != nil {
			fmt.Println("error writing output: ", err)
			return
		}
	}
}
<commit_msg>adding magic bytes<commit_after>package main

import (
	"bufio"
	"encoding/binary"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"sync"

	"github.com/layeh/gopus"
)

// Define constants
const (
	// The current version of the DCA format
	FormatVersion int8 = 1
)

// All global variables used within the program
var (
	// Magic bytes to write at the start of a DCA file
	MagicBytes string = fmt.Sprintf("DCA%d", FormatVersion)

	// 1 for mono, 2 for stereo
	Channels int

	// Must be one of 8000, 12000, 16000, 24000, or 48000.
	// Discord only uses 48000 currently.
	FrameRate int

	// Rates from 500 to 512000 bits per second are meaningful
	// Discord only uses 8000 to 128000 and default is 64000
	Bitrate int

	// Must be one of voip, audio, or lowdelay.
	// DCA defaults to audio which is ideal for music
	// Not sure what Discord uses here, probably voip
	Application string

	FrameSize int // uint16 size of each audio frame
	MaxBytes  int // max size of opus data

	Volume int // change audio volume (256=normal)

	OpusEncoder *gopus.Encoder

	InFile string

	OutFile string = "pipe:1"
	OutBuf  []byte

	EncodeChan chan []int16
	OutputChan chan []byte

	err error

	wg sync.WaitGroup
)

// init configures and parses the command line arguments
func init() {

	flag.StringVar(&InFile, "i", "pipe:0", "infile")
	flag.IntVar(&Volume, "vol", 256, "change audio volume (256=normal)")
	flag.IntVar(&Channels, "ac", 2, "audio channels")
	flag.IntVar(&FrameRate, "ar", 48000, "audio sampling rate")
	flag.IntVar(&FrameSize, "as", 960, "audio frame size can be 960 (20ms), 1920 (40ms), or 2880 (60ms)")
	flag.IntVar(&Bitrate, "ab", 64, "audio encoding bitrate in kb/s can be 8 - 128")
	flag.StringVar(&Application, "aa", "audio", "audio application can be voip, audio, or lowdelay")

	if len(os.Args) < 2 {
		flag.Usage()
		os.Exit(1)
	}

	flag.Parse()

	MaxBytes = (FrameSize * Channels) * 2 // max size of opus data
}

// very simple program that wraps ffmpeg and outputs raw opus data frames
// with a uint16 header for each frame with the frame length in bytes
func main() {

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Basic setup and validation
	//////////////////////////////////////////////////////////////////////////

	// If only one argument provided assume it's a filename.
	if len(os.Args) == 2 {
		InFile = os.Args[1]
	}

	// If reading from a file, verify it exists.
	if InFile != "pipe:0" {

		if _, err := os.Stat(InFile); os.IsNotExist(err) {
			fmt.Println("error: infile does not exist")
			flag.Usage()
			return
		}
	}

	// If reading from pipe, make sure pipe is open
	if InFile == "pipe:0" {
		fi, err := os.Stdin.Stat()
		if err != nil {
			fmt.Println(err)
			return
		}

		if (fi.Mode() & os.ModeCharDevice) == 0 {
		} else {
			fmt.Println("error: stdin is not a pipe.")
			flag.Usage()
			return
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Create chans, buffers, and encoder for use
	//////////////////////////////////////////////////////////////////////////

	// create an opusEncoder to use
	OpusEncoder, err = gopus.NewEncoder(FrameRate, Channels, gopus.Audio)
	if err != nil {
		fmt.Println("NewEncoder Error:", err)
		return
	}

	// set opus encoding options
	//	OpusEncoder.SetVbr(true)                // bool

	if Bitrate < 1 || Bitrate > 512 {
		Bitrate = 64 // Set to Discord default
	}
	OpusEncoder.SetBitrate(Bitrate * 1000)

	switch Application {
	case "voip":
		OpusEncoder.SetApplication(gopus.Voip)
	case "audio":
		OpusEncoder.SetApplication(gopus.Audio)
	case "lowdelay":
		OpusEncoder.SetApplication(gopus.RestrictedLowDelay)
	default:
		OpusEncoder.SetApplication(gopus.Audio)
	}

	OutputChan = make(chan []byte, 10)
	EncodeChan = make(chan []int16, 10)

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Start reader and writer workers
	//////////////////////////////////////////////////////////////////////////

	wg.Add(1)
	go reader()

	wg.Add(1)
	go encoder()

	wg.Add(1)
	go writer()

	// wait for above goroutines to finish, then exit.
	wg.Wait()
}

// reader reads from the input
func reader() {

	defer func() {
		close(EncodeChan)
		wg.Done()
	}()

	// read from file
	if InFile != "pipe:0" {

		// Create a shell command "object" to run.
		ffmpeg := exec.Command("ffmpeg", "-i", InFile, "-vol", strconv.Itoa(Volume), "-f", "s16le", "-ar", strconv.Itoa(FrameRate), "-ac", strconv.Itoa(Channels), "pipe:1")
		stdout, err := ffmpeg.StdoutPipe()
		if err != nil {
			fmt.Println("StdoutPipe Error:", err)
			return
		}

		// Starts the ffmpeg command
		err = ffmpeg.Start()
		if err != nil {
			fmt.Println("RunStart Error:", err)
			return
		}

		for {

			// read data from ffmpeg stdout
			InBuf := make([]int16, FrameSize*Channels)
			err = binary.Read(stdout, binary.LittleEndian, &InBuf)
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				return
			}
			if err != nil {
				fmt.Println("error reading from ffmpeg stdout :", err)
				return
			}

			// write pcm data to the EncodeChan
			EncodeChan <- InBuf

		}
	}

	// read input from stdin pipe
	if InFile == "pipe:0" {

		// 16KB input buffer
		rbuf := bufio.NewReaderSize(os.Stdin, 16384)
		for {

			// read data from stdin
			InBuf := make([]int16, FrameSize*Channels)

			err = binary.Read(rbuf, binary.LittleEndian, &InBuf)
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				return
			}
			if err != nil {
				fmt.Println("error reading from ffmpeg stdout :", err)
				return
			}

			// write pcm data to the EncodeChan
			EncodeChan <- InBuf
		}
	}

}

// encoder listens on the EncodeChan and encodes provided PCM16 data
// to opus, then sends the encoded data to the OutputChan
func encoder() {

	defer func() {
		close(OutputChan)
		wg.Done()
	}()

	for {
		pcm, ok := <-EncodeChan
		if !ok {
			// if chan closed, exit
			return
		}

		// try encoding pcm frame with Opus
		opus, err := OpusEncoder.Encode(pcm, FrameSize, MaxBytes)
		if err != nil {
			fmt.Println("Encoding Error:", err)
			return
		}

		// write opus data to OutputChan
		OutputChan <- opus
	}
}

// writer listens on the OutputChan and writes the output to stdout pipe
// TODO: Add support for writing directly to a file
func writer() {

	defer wg.Done()

	var opuslen uint16
	// 16KB output buffer
	wbuf := bufio.NewWriterSize(os.Stdout, 16384)

	// write the magic bytes
	fmt.Print(MagicBytes)

	for {
		opus, ok := <-OutputChan
		if !ok {
			// if chan closed, exit
			return
		}

		// write header
		opuslen = uint16(len(opus))
		err = binary.Write(wbuf, binary.LittleEndian, &opuslen)
		if err != nil {
			fmt.Println("error writing output: ", err)
			return
		}

		// write opus data to stdout
		err = binary.Write(wbuf, binary.LittleEndian, &opus)
		if err != nil {
			fmt.Println("error writing output: ", err)
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/subtle"
	"encoding/base64"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/opendoor-labs/gothumb/Godeps/_workspace/src/github.com/DAddYE/vips"
	"github.com/opendoor-labs/gothumb/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
	"github.com/opendoor-labs/gothumb/Godeps/_workspace/src/github.com/rlmcpherson/s3gof3r"
)

var (
	listenInterface  string
	maxAge           int
	securityKey      []byte
	resultBucketName string
	useRRS           bool
	unsafeMode       bool

	httpClient   = http.DefaultClient
	resultBucket *s3gof3r.Bucket
)

type ByteSize int64

const (
	_           = iota // ignore first value by assigning to blank identifier
	KB ByteSize = 1 << (10 * iota)
	MB
)

func main() {
	log.SetFlags(0) // hide timestamps from Go logs

	parseFlags()

	resultBucketName = os.Getenv("RESULT_STORAGE_BUCKET")
	if resultBucketName != "" {
		keys, err := s3gof3r.EnvKeys()
		if err != nil {
			log.Fatal(err)
		}
		resultBucket = s3gof3r.New(s3gof3r.DefaultDomain, keys).Bucket(resultBucketName)
		resultBucket.Concurrency = 4
		resultBucket.PartSize = int64(2 * MB)
		resultBucket.Md5Check = false
		httpClient = resultBucket.Client

		if rrs := os.Getenv("USE_RRS"); rrs == "true" || rrs == "1" {
			useRRS = true
		}
	}

	router := httprouter.New()
	router.HEAD("/:signature/:size/*source", handleResize)
	router.GET("/:signature/:size/*source", handleResize)
	log.Fatal(http.ListenAndServe(listenInterface, router))
}

func parseFlags() {
	securityKeyStr := ""

	port := os.Getenv("PORT")
	if port == "" {
		port = "8888"
	}

	if maxAgeStr := os.Getenv("MAX_AGE"); maxAgeStr != "" {
		var err error
		if maxAge, err = strconv.Atoi(maxAgeStr); err != nil {
			log.Fatal("invalid MAX_AGE setting")
		}
	}

	flag.StringVar(&listenInterface, "l", ":"+port, "listen address")
	flag.IntVar(&maxAge, "max-age", maxAge, "the maximum HTTP caching age to use on returned images")
	flag.StringVar(&securityKeyStr, "k", os.Getenv("SECURITY_KEY"), "security key")
	flag.BoolVar(&unsafeMode, "unsafe", false, "whether to allow /unsafe URLs")

	flag.Parse()

	if securityKeyStr == "" && !unsafeMode {
		log.Fatalf("must provide a security key with -k or allow unsafe URLs")
	}
	securityKey = []byte(securityKeyStr)
}

func handleResize(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	reqPath := req.URL.EscapedPath()
	log.Printf("%s %s", req.Method, reqPath)
	sourceURL, err := url.Parse(strings.TrimPrefix(params.ByName("source"), "/"))
	if err != nil || !(sourceURL.Scheme == "http" || sourceURL.Scheme == "https") {
		http.Error(w, "invalid source URL", 400)
		return
	}

	sig := params.ByName("signature")
	pathToVerify := strings.TrimPrefix(reqPath, "/"+sig+"/")
	if err := validateSignature(sig, pathToVerify); err != nil {
		http.Error(w, "invalid signature", 401)
		return
	}

	width, height, err := parseWidthAndHeight(params.ByName("size"))
	if err != nil {
		http.Error(w, "invalid height requested", 400)
		return
	}

	resultPath := normalizePath(strings.TrimPrefix(reqPath, "/"+sig))

	// TODO(bgentry): everywhere that switches on resultBucket should switch on
	// something like resultStorage instead.
	if resultBucket == nil {
		// no result storage, just generate the thumbnail
		generateThumbnail(w, req.Method, resultPath, sourceURL.String(), width, height)
		return
	}

	// try to get stored result
	r, h, err := getStoredResult(req.Method, resultPath)
	if err != nil {
		log.Printf("getting stored result: %s", err)
		generateThumbnail(w, req.Method, resultPath, sourceURL.String(), width, height)
		return
	}
	defer r.Close()

	// return stored result
	length, err := strconv.Atoi(h.Get("Content-Length"))
	if err != nil {
		log.Printf("invalid result content-length: %s", err)
		// TODO: try to generate instead of erroring w/ 500?
		http.Error(w, err.Error(), 500)
		return
	}

	setResultHeaders(w, &result{
		ContentType:   h.Get("Content-Type"),
		ContentLength: length,
		ETag:          strings.Trim(h.Get("Etag"), `"`),
		Path:          resultPath,
	})
	if _, err = io.Copy(w, r); err != nil {
		log.Printf("copying from stored result: %s", err)
		return
	}
	if err = r.Close(); err != nil {
		log.Printf("closing stored result copy: %s", err)
	}
}

type result struct {
	Data          []byte
	ContentType   string
	ContentLength int
	ETag          string
	Path          string
}

func computeHexMD5(data []byte) string {
	h := md5.New()
	h.Write(data)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func generateThumbnail(w http.ResponseWriter, rmethod, rpath string, sourceURL string, width, height uint) {
	log.Printf("generating %s", rpath)
	resp, err := httpClient.Get(sourceURL)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		log.Printf("unexpected status code from source: %d", resp.StatusCode)
		http.Error(w, "", resp.StatusCode)
		return
	}

	img, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	buf, err := vips.Resize(img, vips.Options{
		Height:       int(height),
		Width:        int(width),
		Crop:         true,
		Interpolator: vips.BICUBIC,
		Gravity:      vips.CENTRE,
		Quality:      50,
	})
	if err != nil {
		http.Error(w, fmt.Sprintf("resizing image: %s", err.Error()), 500)
		return
	}

	res := &result{
		ContentType:   "image/jpeg", // TODO: support PNGs as well
		ContentLength: len(buf),
		Data:          buf, // TODO: check if I need to copy this
		ETag:          computeHexMD5(buf),
		Path:          rpath,
	}
	setResultHeaders(w, res)
	if rmethod != "HEAD" {
		if _, err = w.Write(buf); err != nil {
			log.Printf("writing buffer to response: %s", err)
		}
	}

	if resultBucket != nil {
		go storeResult(res)
	}
}

// caller is responsible for closing the returned ReadCloser
func getStoredResult(method, path string) (io.ReadCloser, http.Header, error) {
	if method != "HEAD" {
		return resultBucket.GetReader(path, nil)
	}

	s3URL := fmt.Sprintf("https://%s.s3.amazonaws.com%s", resultBucketName, path)
	req, err := http.NewRequest(method, s3URL, nil)
	if err != nil {
		return nil, nil, err
	}

	resultBucket.Sign(req)
	res, err := httpClient.Do(req)
	if err != nil {
		return nil, nil, err
	}
	if res.StatusCode < 200 || res.StatusCode >= 300 {
		// TODO: drain res.Body to ioutil.Discard before closing?
		res.Body.Close()
		return nil, nil, fmt.Errorf("unexpected status code %d", res.StatusCode)
	}
	res.Header.Set("Content-Length", strconv.FormatInt(res.ContentLength, 10))
	return res.Body, res.Header, err
}

func mustGetenv(name string) string {
	value := os.Getenv(name)
	if value == "" {
		log.Fatalf("missing %s env", name)
	}
	return value
}

func normalizePath(p string) string {
	// TODO(bgentry): Support for custom root path? ala RESULT_STORAGE_AWS_STORAGE_ROOT_PATH
	return path.Clean(p)
}

func parseWidthAndHeight(str string) (width, height uint, err error) {
	sizeParts := strings.Split(str, "x")
	if len(sizeParts) != 2 {
		err = fmt.Errorf("invalid size requested")
		return
	}
	width64, err := strconv.ParseUint(sizeParts[0], 10, 64)
	if err != nil {
		err = fmt.Errorf("invalid width requested")
		return
	}
	height64, err := strconv.ParseUint(sizeParts[1], 10, 64)
	if err != nil {
		err = fmt.Errorf("invalid height requested")
		return
	}
	return uint(width64), uint(height64), nil
}

func setCacheHeaders(w http.ResponseWriter) {
	w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d,public", maxAge))
	w.Header().Set("Expires", time.Now().UTC().Add(time.Duration(maxAge)*time.Second).Format(http.TimeFormat))
}

func setResultHeaders(w http.ResponseWriter, result *result) {
	w.Header().Set("Content-Type", result.ContentType)
	w.Header().Set("Content-Length", strconv.Itoa(result.ContentLength))
	w.Header().Set("ETag", `"`+result.ETag+`"`)
	setCacheHeaders(w)
}

func storeResult(res *result) {
	h := make(http.Header)
	h.Set("Content-Type", res.ContentType)
	if useRRS {
		h.Set("x-amz-storage-class", "REDUCED_REDUNDANCY")
	}
	w, err := resultBucket.PutWriter(res.Path, h, nil)
	if err != nil {
		log.Printf("storing result for %s: %s", res.Path, err)
		return
	}
	defer w.Close()
	if _, err = w.Write(res.Data); err != nil {
		log.Printf("storing result for %s: %s", res.Path, err)
		return
	}
	if err = w.Close(); err != nil {
		log.Printf("storing result for %s: %s", res.Path, err)
	}
}

func validateSignature(sig, pathPart string) error {
	if unsafeMode && sig == "unsafe" {
		return nil
	}

	h := hmac.New(sha1.New, securityKey)
	if _, err := h.Write([]byte(pathPart)); err != nil {
		return err
	}
	actualSig := base64.URLEncoding.EncodeToString(h.Sum(nil))
	// constant-time string comparison
	if subtle.ConstantTimeCompare([]byte(sig), []byte(actualSig)) != 1 {
		return fmt.Errorf("signature mismatch")
	}
	return nil
}
<commit_msg>return a 400 when image format is unknown<commit_after>package main

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/subtle"
	"encoding/base64"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/opendoor-labs/gothumb/Godeps/_workspace/src/github.com/DAddYE/vips"
	"github.com/opendoor-labs/gothumb/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
	"github.com/opendoor-labs/gothumb/Godeps/_workspace/src/github.com/rlmcpherson/s3gof3r"
)

var (
	listenInterface  string
	maxAge           int
	securityKey      []byte
	resultBucketName string
	useRRS           bool
	unsafeMode       bool

	httpClient   = http.DefaultClient
	resultBucket *s3gof3r.Bucket
)

type ByteSize int64

const (
	_           = iota // ignore first value by assigning to blank identifier
	KB ByteSize = 1 << (10 * iota)
	MB
)

func main() {
	log.SetFlags(0) // hide timestamps from Go logs

	parseFlags()

	resultBucketName = os.Getenv("RESULT_STORAGE_BUCKET")
	if resultBucketName != "" {
		keys, err := s3gof3r.EnvKeys()
		if err != nil {
			log.Fatal(err)
		}
		resultBucket = s3gof3r.New(s3gof3r.DefaultDomain, keys).Bucket(resultBucketName)
		resultBucket.Concurrency = 4
		resultBucket.PartSize = int64(2 * MB)
		resultBucket.Md5Check = false
		httpClient = resultBucket.Client

		if rrs := os.Getenv("USE_RRS"); rrs == "true" || rrs == "1" {
			useRRS = true
		}
	}

	router := httprouter.New()
	router.HEAD("/:signature/:size/*source", handleResize)
	router.GET("/:signature/:size/*source", handleResize)
	log.Fatal(http.ListenAndServe(listenInterface, router))
}

func parseFlags() {
	securityKeyStr := ""

	port := os.Getenv("PORT")
	if port == "" {
		port = "8888"
	}

	if maxAgeStr := os.Getenv("MAX_AGE"); maxAgeStr != "" {
		var err error
		if maxAge, err = strconv.Atoi(maxAgeStr); err != nil {
			log.Fatal("invalid MAX_AGE setting")
		}
	}

	flag.StringVar(&listenInterface, "l", ":"+port, "listen address")
	flag.IntVar(&maxAge, "max-age", maxAge, "the maximum HTTP caching age to use on returned images")
	flag.StringVar(&securityKeyStr, "k", os.Getenv("SECURITY_KEY"), "security key")
	flag.BoolVar(&unsafeMode, "unsafe", false, "whether to allow /unsafe URLs")

	flag.Parse()

	if securityKeyStr == "" && !unsafeMode {
		log.Fatalf("must provide a security key with -k or allow unsafe URLs")
	}
	securityKey = []byte(securityKeyStr)
}

func handleResize(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	reqPath := req.URL.EscapedPath()
	log.Printf("%s %s", req.Method, reqPath)
	sourceURL, err := url.Parse(strings.TrimPrefix(params.ByName("source"), "/"))
	if err != nil || !(sourceURL.Scheme == "http" || sourceURL.Scheme == "https") {
		http.Error(w, "invalid source URL", 400)
		return
	}

	sig := params.ByName("signature")
	pathToVerify := strings.TrimPrefix(reqPath, "/"+sig+"/")
	if err := validateSignature(sig, pathToVerify); err != nil {
		http.Error(w, "invalid signature", 401)
		return
	}

	width, height, err := parseWidthAndHeight(params.ByName("size"))
	if err != nil {
		http.Error(w, "invalid height requested", 400)
		return
	}

	resultPath := normalizePath(strings.TrimPrefix(reqPath, "/"+sig))

	// TODO(bgentry): everywhere that switches on resultBucket should switch on
	// something like resultStorage instead.
	if resultBucket == nil {
		// no result storage, just generate the thumbnail
		generateThumbnail(w, req.Method, resultPath, sourceURL.String(), width, height)
		return
	}

	// try to get stored result
	r, h, err := getStoredResult(req.Method, resultPath)
	if err != nil {
		log.Printf("getting stored result: %s", err)
		generateThumbnail(w, req.Method, resultPath, sourceURL.String(), width, height)
		return
	}
	defer r.Close()

	// return stored result
	length, err := strconv.Atoi(h.Get("Content-Length"))
	if err != nil {
		log.Printf("invalid result content-length: %s", err)
		// TODO: try to generate instead of erroring w/ 500?
		http.Error(w, err.Error(), 500)
		return
	}

	setResultHeaders(w, &result{
		ContentType:   h.Get("Content-Type"),
		ContentLength: length,
		ETag:          strings.Trim(h.Get("Etag"), `"`),
		Path:          resultPath,
	})
	if _, err = io.Copy(w, r); err != nil {
		log.Printf("copying from stored result: %s", err)
		return
	}
	if err = r.Close(); err != nil {
		log.Printf("closing stored result copy: %s", err)
	}
}

type result struct {
	Data          []byte
	ContentType   string
	ContentLength int
	ETag          string
	Path          string
}

func computeHexMD5(data []byte) string {
	h := md5.New()
	h.Write(data)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func generateThumbnail(w http.ResponseWriter, rmethod, rpath string, sourceURL string, width, height uint) {
	log.Printf("generating %s", rpath)
	resp, err := httpClient.Get(sourceURL)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		log.Printf("unexpected status code from source: %d", resp.StatusCode)
		http.Error(w, "", resp.StatusCode)
		return
	}

	img, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	buf, err := vips.Resize(img, vips.Options{
		Height:       int(height),
		Width:        int(width),
		Crop:         true,
		Interpolator: vips.BICUBIC,
		Gravity:      vips.CENTRE,
		Quality:      50,
	})
	if err != nil {
		responseCode := 500
		if err.Error() == "unknown image format" {
			responseCode = 400
		}
		http.Error(w, fmt.Sprintf("resizing image: %s", err.Error()), responseCode)
		return
	}

	res := &result{
		ContentType:   "image/jpeg", // TODO: support PNGs as well
		ContentLength: len(buf),
		Data:          buf, // TODO: check if I need to copy this
		ETag:          computeHexMD5(buf),
		Path:          rpath,
	}
	setResultHeaders(w, res)
	if rmethod != "HEAD" {
		if _, err = w.Write(buf); err != nil {
			log.Printf("writing buffer to response: %s", err)
		}
	}

	if resultBucket != nil {
		go storeResult(res)
	}
}

// caller is responsible for closing the returned ReadCloser
func getStoredResult(method, path string) (io.ReadCloser, http.Header, error) {
	if method != "HEAD" {
		return resultBucket.GetReader(path, nil)
	}

	s3URL := fmt.Sprintf("https://%s.s3.amazonaws.com%s", resultBucketName, path)
	req, err := http.NewRequest(method, s3URL, nil)
	if err != nil {
		return nil, nil, err
	}

	resultBucket.Sign(req)
	res, err := httpClient.Do(req)
	if err != nil {
		return nil, nil, err
	}
	if res.StatusCode < 200 || res.StatusCode >= 300 {
		// TODO: drain res.Body to ioutil.Discard before closing?
		res.Body.Close()
		return nil, nil, fmt.Errorf("unexpected status code %d", res.StatusCode)
	}
	res.Header.Set("Content-Length", strconv.FormatInt(res.ContentLength, 10))
	return res.Body, res.Header, err
}

func mustGetenv(name string) string {
	value := os.Getenv(name)
	if value == "" {
		log.Fatalf("missing %s env", name)
	}
	return value
}

func normalizePath(p string) string {
	// TODO(bgentry): Support for custom root path? ala RESULT_STORAGE_AWS_STORAGE_ROOT_PATH
	return path.Clean(p)
}

func parseWidthAndHeight(str string) (width, height uint, err error) {
	sizeParts := strings.Split(str, "x")
	if len(sizeParts) != 2 {
		err = fmt.Errorf("invalid size requested")
		return
	}
	width64, err := strconv.ParseUint(sizeParts[0], 10, 64)
	if err != nil {
		err = fmt.Errorf("invalid width requested")
		return
	}
	height64, err := strconv.ParseUint(sizeParts[1], 10, 64)
	if err != nil {
		err = fmt.Errorf("invalid height requested")
		return
	}
	return uint(width64), uint(height64), nil
}

func setCacheHeaders(w http.ResponseWriter) {
	w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d,public", maxAge))
	w.Header().Set("Expires", time.Now().UTC().Add(time.Duration(maxAge)*time.Second).Format(http.TimeFormat))
}

func setResultHeaders(w http.ResponseWriter, result *result) {
	w.Header().Set("Content-Type", result.ContentType)
	w.Header().Set("Content-Length", strconv.Itoa(result.ContentLength))
	w.Header().Set("ETag", `"`+result.ETag+`"`)
	setCacheHeaders(w)
}

func storeResult(res *result) {
	h := make(http.Header)
	h.Set("Content-Type", res.ContentType)
	if useRRS {
		h.Set("x-amz-storage-class", "REDUCED_REDUNDANCY")
	}
	w, err := resultBucket.PutWriter(res.Path, h, nil)
	if err != nil {
		log.Printf("storing result for %s: %s", res.Path, err)
		return
	}
	defer w.Close()
	if _, err = w.Write(res.Data); err != nil {
		log.Printf("storing result for %s: %s", res.Path, err)
		return
	}
	if err = w.Close(); err != nil {
		log.Printf("storing result for %s: %s", res.Path, err)
	}
}

func validateSignature(sig, pathPart string) error {
	if unsafeMode && sig == "unsafe" {
		return nil
	}

	h := hmac.New(sha1.New, securityKey)
	if _, err := h.Write([]byte(pathPart)); err != nil {
		return err
	}
	actualSig := base64.URLEncoding.EncodeToString(h.Sum(nil))
	// constant-time string comparison
	if subtle.ConstantTimeCompare([]byte(sig), []byte(actualSig)) != 1 {
		return fmt.Errorf("signature mismatch")
	}
	return nil
}
<|endoftext|>


<commit_before>package worker

import (
	"fmt"
	"sync"
	"time"
)

type Tasker interface {
	Do(c chan bool)
}

type Work struct {
	debug     bool
	sleepTime time.Duration
	lock      sync.Mutex
	limit     int
	taskList  []Tasker
}

// 设置没有任务的时候的睡眠时间
func (w *Work) SetSleepTime(t time.Duration) {
	w.sleepTime = t
}

// 设置是否为debug
func (w *Work) Debug(debug bool) *Work {
	w.debug = debug
	return w
}

// 设置goroutine速度
func (w *Work) SetLimit(limit int) {
	w.limit = limit
}

// 增加一个任务到列表里面
func (w *Work) AddTask(t Tasker) {
	w.taskList = append(w.taskList, t)
	if w.debug {
		fmt.Println("增加了一个任务，现在任务总个数为：", len(w.taskList))
	}
}

// 获取一个任务，以及是否获取成功
func (w *Work) getTask() (Tasker, bool) {
	w.lock.Lock()
	defer w.lock.Unlock()
	if len(w.taskList) == 0 {
		return nil, false
	}
	t := w.taskList[0]
	w.taskList = w.taskList[1:]
	return t, true
}

// 任务开始
func (w *Work) Start() {
	c := make(chan bool, w.limit)

	for i := 0; i < w.limit; i++ {
		for {
			t, ok := w.getTask()
			if ok {
				go t.Do(c)
				break
			}
			time.Sleep(w.sleepTime)
		}
	}
	for {
		<-c
		t, ok := w.getTask()
		if ok {
			go t.Do(c)
		}
		time.Sleep(w.sleepTime)
	}

}

// 返回任务列表现有的个数
func (w *Work) Len() int {
	return len(w.taskList)
}

// 创建一个Work
func NewWork() *Work {
	w := new(Work)
	w.sleepTime = time.Second
	w.limit = 1
	return w
}
<commit_msg>将两种等待时间分开 增加log函数方便调试<commit_after><|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"

	"github.com/ark-lang/ark-go/common"
	"github.com/ark-lang/ark-go/lexer"
	"github.com/ark-lang/ark-go/parser"
	//"github.com/ark-lang/ark-go/codegen"
	//"github.com/ark-lang/ark-go/codegen/LLVMCodegen"
)

var versionFlag = flag.Bool("version", false, "show version information")
var verboseFlag = flag.Bool("v", false, "enable verbose mode")
var inputFlag = flag.String("input", "", "input file")
var outputFlag = flag.String("output", "", "output file")

func main() {
	flag.Parse()

	if *versionFlag {
		version()
		return
	}

	verbose := *verboseFlag

	sourcefiles := make([]*common.Sourcefile, 0)
	input, err := common.NewSourcefile(*inputFlag)
	check(err)
	sourcefiles = append(sourcefiles, input)

	for _, file := range sourcefiles {
		file.Tokens = lexer.Lex(file.Contents, *inputFlag, verbose)
	}

	parsedFiles := make([]*parser.File, 0)
	for _, file := range sourcefiles {
		parsedFiles = append(parsedFiles, parser.Parse(file.Tokens, verbose))
	}

	//gen := &LLVMCodegen.LLVMCodegen {}
	//gen.Generate()
}

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

func version() {
	fmt.Println("ark-go 2015 - experimental")
}
<commit_msg>will compile multiple files, but no actual arguments anymore so it defaults as verbose<commit_after>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/ark-lang/ark-go/common"
	"github.com/ark-lang/ark-go/lexer"
	"github.com/ark-lang/ark-go/parser"
	//"github.com/ark-lang/ark-go/codegen"
	//"github.com/ark-lang/ark-go/codegen/LLVMCodegen"
)

func main() {
	verbose := true

	sourcefiles := make([]*common.Sourcefile, 0)

	arguments := os.Args[1:]
	for _, arg := range arguments {
		if strings.HasSuffix(arg, ".ark") {
			input, err := common.NewSourcefile(arg)
			check(err)
			sourcefiles = append(sourcefiles, input)
		} else {
			fmt.Println("unknown command")
		}
	}

	for _, file := range sourcefiles {
		file.Tokens = lexer.Lex(file.Contents, file.Filename, verbose)
	}

	parsedFiles := make([]*parser.File, 0)
	for _, file := range sourcefiles {
		parsedFiles = append(parsedFiles, parser.Parse(file.Tokens, verbose))
	}

	//gen := &LLVMCodegen.LLVMCodegen {}
	//gen.Generate()
}

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

func version() {
	fmt.Println("ark-go 2015 - experimental")
}
<|endoftext|>


<commit_before>package main

import "github.com/go-martini/martini"
import "github.com/codegangsta/martini-contrib/render"

import "thingstodo/controllers"
import "thingstodo/db"

func main() {

    m := martini.Classic()

    // Setup middleware
    //m.Use(martini.Recovery())
    //m.Use(martini.Logger())
    m.Use(db.DB())
    m.Use(render.Renderer())

    // Setup routes
    m.Get(`/event`, controllers.GetAllEvents)
    //r.Get(`/event/:id`, GetEvent)
    //r.Post(`/albums`, AddEvent)
    //r.Put(`/albums/:id`, UpdateEvent)
    //r.Delete(`/albums/:id`, DeleteEvent)

    // Add the router action
    m.Run()
}<commit_msg>Update main.go<commit_after>package main

import "github.com/go-martini/martini"
import "github.com/codegangsta/martini-contrib/render"

import "thingstodo/controllers"
import "thingstodo/db"

func main() {

	m := martini.Classic()

	// Setup middleware
	//m.Use(martini.Recovery())
	//m.Use(martini.Logger())
	m.Use(db.DB())
	m.Use(render.Renderer())

	// Setup routes
	m.Get(`/event`, controllers.GetAllEvents)
	//r.Get(`/event/:id`, GetEvent)
	//r.Post(`/albums`, AddEvent)
	//r.Put(`/albums/:id`, UpdateEvent)
	//r.Delete(`/albums/:id`, DeleteEvent)

	// Add the router action
	m.Run()
}
<|endoftext|>


<commit_before>// Package main defines a command line interface for the sqlboiler package
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/friendsofgo/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/volatiletech/sqlboiler/v4/boilingcore"
	"github.com/volatiletech/sqlboiler/v4/drivers"
	"github.com/volatiletech/sqlboiler/v4/importers"
)

//go:generate go-bindata -nometadata -pkg templatebin -o templatebin/bindata.go templates templates/singleton templates_test templates_test/singleton

const sqlBoilerVersion = "4.2.0"

var (
	flagConfigFile string
	cmdState       *boilingcore.State
	cmdConfig      *boilingcore.Config
)

func initConfig() {
	if len(flagConfigFile) != 0 {
		viper.SetConfigFile(flagConfigFile)
		if err := viper.ReadInConfig(); err != nil {
			fmt.Println("Can't read config:", err)
			os.Exit(1)
		}
		return
	}

	var err error
	viper.SetConfigName("sqlboiler")

	configHome := os.Getenv("XDG_CONFIG_HOME")
	homePath := os.Getenv("HOME")
	wd, err := os.Getwd()
	if err != nil {
		wd = "."
	}

	configPaths := []string{wd}
	if len(configHome) > 0 {
		configPaths = append(configPaths, filepath.Join(configHome, "sqlboiler"))
	} else {
		configPaths = append(configPaths, filepath.Join(homePath, ".config/sqlboiler"))
	}

	for _, p := range configPaths {
		viper.AddConfigPath(p)
	}

	// Ignore errors here, fallback to other validation methods.
	// Users can use environment variables if a config is not found.
	_ = viper.ReadInConfig()
}

func main() {
	// Too much happens between here and cobra's argument handling, for
	// something so simple just do it immediately.
	for _, arg := range os.Args {
		if arg == "--version" {
			fmt.Println("SQLBoiler v" + sqlBoilerVersion)
			return
		}
	}

	// Set up the cobra root command
	var rootCmd = &cobra.Command{
		Use:   "sqlboiler [flags] <driver>",
		Short: "SQL Boiler generates an ORM tailored to your database schema.",
		Long: "SQL Boiler generates a Go ORM from template files, tailored to your database schema.\n" +
			`Complete documentation is available at http://github.com/volatiletech/sqlboiler`,
		Example:       `sqlboiler psql`,
		PreRunE:       preRun,
		RunE:          run,
		PostRunE:      postRun,
		SilenceErrors: true,
		SilenceUsage:  true,
	}

	cobra.OnInitialize(initConfig)

	// Set up the cobra root command flags
	rootCmd.PersistentFlags().StringVarP(&flagConfigFile, "config", "c", "", "Filename of config file to override default lookup")
	rootCmd.PersistentFlags().StringP("output", "o", "models", "The name of the folder to output to")
	rootCmd.PersistentFlags().StringP("pkgname", "p", "models", "The name you wish to assign to your generated package")
	rootCmd.PersistentFlags().StringSliceP("templates", "", nil, "A templates directory, overrides the bindata'd template folders in sqlboiler")
	rootCmd.PersistentFlags().StringSliceP("tag", "t", nil, "Struct tags to be included on your models in addition to json, yaml, toml")
	rootCmd.PersistentFlags().StringSliceP("replace", "", nil, "Replace templates by directory: relpath/to_file.tpl:relpath/to_replacement.tpl")
	rootCmd.PersistentFlags().BoolP("debug", "d", false, "Debug mode prints stack traces on error")
	rootCmd.PersistentFlags().BoolP("no-context", "", false, "Disable context.Context usage in the generated code")
	rootCmd.PersistentFlags().BoolP("no-tests", "", false, "Disable generated go test files")
	rootCmd.PersistentFlags().BoolP("no-hooks", "", false, "Disable hooks feature for your models")
	rootCmd.PersistentFlags().BoolP("no-rows-affected", "", false, "Disable rows affected in the generated API")
	rootCmd.PersistentFlags().BoolP("no-auto-timestamps", "", false, "Disable automatic timestamps for created_at/updated_at")
	rootCmd.PersistentFlags().BoolP("no-driver-templates", "", false, "Disable parsing of templates defined by the database driver")
	rootCmd.PersistentFlags().BoolP("no-back-referencing", "", false, "Disable back referencing in the loaded relationship structs")
	rootCmd.PersistentFlags().BoolP("add-global-variants", "", false, "Enable generation for global variants")
	rootCmd.PersistentFlags().BoolP("add-panic-variants", "", false, "Enable generation for panic variants")
	rootCmd.PersistentFlags().BoolP("add-soft-deletes", "", false, "Enable soft deletion by updating deleted_at timestamp")
	rootCmd.PersistentFlags().BoolP("version", "", false, "Print the version")
	rootCmd.PersistentFlags().BoolP("wipe", "", false, "Delete the output folder (rm -rf) before generation to ensure sanity")
	rootCmd.PersistentFlags().StringP("struct-tag-casing", "", "snake", "Decides the casing for go structure tag names. camel, title or snake (default snake)")
	rootCmd.PersistentFlags().StringP("relation-tag", "r", "-", "Relationship struct tag name")
	rootCmd.PersistentFlags().StringSliceP("tag-ignore", "", nil, "List of column names that should have tags values set to '-' (ignored during parsing)")

	// hide flags not recommended for use
	rootCmd.PersistentFlags().MarkHidden("replace")

	viper.BindPFlags(rootCmd.PersistentFlags())
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	viper.AutomaticEnv()

	if err := rootCmd.Execute(); err != nil {
		if e, ok := err.(commandFailure); ok {
			fmt.Printf("Error: %v\n\n", string(e))
			rootCmd.Help()
		} else if !viper.GetBool("debug") {
			fmt.Printf("Error: %v\n", err)
		} else {
			fmt.Printf("Error: %+v\n", err)
		}

		os.Exit(1)
	}
}

type commandFailure string

func (c commandFailure) Error() string {
	return string(c)
}

func preRun(cmd *cobra.Command, args []string) error {
	var err error

	if len(args) == 0 {
		return commandFailure("must provide a driver name")
	}

	driverName := args[0]
	driverPath := args[0]

	if strings.ContainsRune(driverName, os.PathSeparator) {
		driverName = strings.Replace(filepath.Base(driverName), "sqlboiler-", "", 1)
		driverName = strings.Replace(driverName, ".exe", "", 1)
	} else {
		driverPath = "sqlboiler-" + driverPath
		if p, err := exec.LookPath(driverPath); err == nil {
			driverPath = p
		}
	}

	driverPath, err = filepath.Abs(driverPath)
	if err != nil {
		return errors.Wrap(err, "could not find absolute path to driver")
	}
	drivers.RegisterBinary(driverName, driverPath)

	cmdConfig = &boilingcore.Config{
		DriverName:        driverName,
		OutFolder:         viper.GetString("output"),
		PkgName:           viper.GetString("pkgname"),
		Debug:             viper.GetBool("debug"),
		AddGlobal:         viper.GetBool("add-global-variants"),
		AddPanic:          viper.GetBool("add-panic-variants"),
		AddSoftDeletes:    viper.GetBool("add-soft-deletes"),
		NoContext:         viper.GetBool("no-context"),
		NoTests:           viper.GetBool("no-tests"),
		NoHooks:           viper.GetBool("no-hooks"),
		NoRowsAffected:    viper.GetBool("no-rows-affected"),
		NoAutoTimestamps:  viper.GetBool("no-auto-timestamps"),
		NoDriverTemplates: viper.GetBool("no-driver-templates"),
		NoBackReferencing: viper.GetBool("no-back-referencing"),
		Wipe:              viper.GetBool("wipe"),
		StructTagCasing:   strings.ToLower(viper.GetString("struct-tag-casing")), // camel | snake | title
		TagIgnore:         viper.GetStringSlice("tag-ignore"),
		RelationTag:       viper.GetString("relation-tag"),
		TemplateDirs:      viper.GetStringSlice("templates"),
		Tags:              viper.GetStringSlice("tag"),
		Replacements:      viper.GetStringSlice("replace"),
		Aliases:           boilingcore.ConvertAliases(viper.Get("aliases")),
		TypeReplaces:      boilingcore.ConvertTypeReplace(viper.Get("types")),
		Version:           sqlBoilerVersion,
	}

	if cmdConfig.Debug {
		fmt.Fprintln(os.Stderr, "using driver:", driverPath)
	}

	// Configure the driver
	cmdConfig.DriverConfig = map[string]interface{}{
		"whitelist": viper.GetStringSlice(driverName + ".whitelist"),
		"blacklist": viper.GetStringSlice(driverName + ".blacklist"),
	}

	keys := allKeys(driverName)
	for _, key := range keys {
		if key != "blacklist" && key != "whitelist" {
			prefixedKey := fmt.Sprintf("%s.%s", driverName, key)
			cmdConfig.DriverConfig[key] = viper.Get(prefixedKey)
		}
	}

	cmdConfig.Imports = configureImports()

	cmdState, err = boilingcore.New(cmdConfig)
	return err
}

func configureImports() importers.Collection {
	imports := importers.NewDefaultImports()

	mustMap := func(m importers.Map, err error) importers.Map {
		if err != nil {
			panic("failed to change viper interface into importers.Map: " + err.Error())
		}

		return m
	}

	if viper.IsSet("imports.all.standard") {
		imports.All.Standard = viper.GetStringSlice("imports.all.standard")
	}
	if viper.IsSet("imports.all.third_party") {
		imports.All.ThirdParty = viper.GetStringSlice("imports.all.third_party")
	}
	if viper.IsSet("imports.test.standard") {
		imports.Test.Standard = viper.GetStringSlice("imports.test.standard")
	}
	if viper.IsSet("imports.test.third_party") {
		imports.Test.ThirdParty = viper.GetStringSlice("imports.test.third_party")
	}
	if viper.IsSet("imports.singleton") {
		imports.Singleton = mustMap(importers.MapFromInterface(viper.Get("imports.singleton")))
	}
	if viper.IsSet("imports.test_singleton") {
		imports.TestSingleton = mustMap(importers.MapFromInterface(viper.Get("imports.test_singleton")))
	}
	if viper.IsSet("imports.based_on_type") {
		imports.BasedOnType = mustMap(importers.MapFromInterface(viper.Get("imports.based_on_type")))
	}

	return imports
}

func run(cmd *cobra.Command, args []string) error {
	return cmdState.Run()
}

func postRun(cmd *cobra.Command, args []string) error {
	return cmdState.Cleanup()
}

func allKeys(prefix string) []string {
	keys := make(map[string]bool)

	prefix += "."

	for _, e := range os.Environ() {
		splits := strings.SplitN(e, "=", 2)
		key := strings.ReplaceAll(strings.ToLower(splits[0]), "_", ".")

		if strings.HasPrefix(key, prefix) {
			keys[strings.ReplaceAll(key, prefix, "")] = true
		}
	}

	for _, key := range viper.AllKeys() {
		if strings.HasPrefix(key, prefix) {
			keys[strings.ReplaceAll(key, prefix, "")] = true
		}
	}

	keySlice := make([]string, 0, len(keys))
	for k := range keys {
		keySlice = append(keySlice, k)
	}
	return keySlice
}
<commit_msg>Bump version<commit_after>// Package main defines a command line interface for the sqlboiler package
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/friendsofgo/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/volatiletech/sqlboiler/v4/boilingcore"
	"github.com/volatiletech/sqlboiler/v4/drivers"
	"github.com/volatiletech/sqlboiler/v4/importers"
)

//go:generate go-bindata -nometadata -pkg templatebin -o templatebin/bindata.go templates templates/singleton templates_test templates_test/singleton

const sqlBoilerVersion = "4.3.0"

var (
	flagConfigFile string
	cmdState       *boilingcore.State
	cmdConfig      *boilingcore.Config
)

func initConfig() {
	if len(flagConfigFile) != 0 {
		viper.SetConfigFile(flagConfigFile)
		if err := viper.ReadInConfig(); err != nil {
			fmt.Println("Can't read config:", err)
			os.Exit(1)
		}
		return
	}

	var err error
	viper.SetConfigName("sqlboiler")

	configHome := os.Getenv("XDG_CONFIG_HOME")
	homePath := os.Getenv("HOME")
	wd, err := os.Getwd()
	if err != nil {
		wd = "."
	}

	configPaths := []string{wd}
	if len(configHome) > 0 {
		configPaths = append(configPaths, filepath.Join(configHome, "sqlboiler"))
	} else {
		configPaths = append(configPaths, filepath.Join(homePath, ".config/sqlboiler"))
	}

	for _, p := range configPaths {
		viper.AddConfigPath(p)
	}

	// Ignore errors here, fallback to other validation methods.
	// Users can use environment variables if a config is not found.
	_ = viper.ReadInConfig()
}

func main() {
	// Too much happens between here and cobra's argument handling, for
	// something so simple just do it immediately.
	for _, arg := range os.Args {
		if arg == "--version" {
			fmt.Println("SQLBoiler v" + sqlBoilerVersion)
			return
		}
	}

	// Set up the cobra root command
	var rootCmd = &cobra.Command{
		Use:   "sqlboiler [flags] <driver>",
		Short: "SQL Boiler generates an ORM tailored to your database schema.",
		Long: "SQL Boiler generates a Go ORM from template files, tailored to your database schema.\n" +
			`Complete documentation is available at http://github.com/volatiletech/sqlboiler`,
		Example:       `sqlboiler psql`,
		PreRunE:       preRun,
		RunE:          run,
		PostRunE:      postRun,
		SilenceErrors: true,
		SilenceUsage:  true,
	}

	cobra.OnInitialize(initConfig)

	// Set up the cobra root command flags
	rootCmd.PersistentFlags().StringVarP(&flagConfigFile, "config", "c", "", "Filename of config file to override default lookup")
	rootCmd.PersistentFlags().StringP("output", "o", "models", "The name of the folder to output to")
	rootCmd.PersistentFlags().StringP("pkgname", "p", "models", "The name you wish to assign to your generated package")
	rootCmd.PersistentFlags().StringSliceP("templates", "", nil, "A templates directory, overrides the bindata'd template folders in sqlboiler")
	rootCmd.PersistentFlags().StringSliceP("tag", "t", nil, "Struct tags to be included on your models in addition to json, yaml, toml")
	rootCmd.PersistentFlags().StringSliceP("replace", "", nil, "Replace templates by directory: relpath/to_file.tpl:relpath/to_replacement.tpl")
	rootCmd.PersistentFlags().BoolP("debug", "d", false, "Debug mode prints stack traces on error")
	rootCmd.PersistentFlags().BoolP("no-context", "", false, "Disable context.Context usage in the generated code")
	rootCmd.PersistentFlags().BoolP("no-tests", "", false, "Disable generated go test files")
	rootCmd.PersistentFlags().BoolP("no-hooks", "", false, "Disable hooks feature for your models")
	rootCmd.PersistentFlags().BoolP("no-rows-affected", "", false, "Disable rows affected in the generated API")
	rootCmd.PersistentFlags().BoolP("no-auto-timestamps", "", false, "Disable automatic timestamps for created_at/updated_at")
	rootCmd.PersistentFlags().BoolP("no-driver-templates", "", false, "Disable parsing of templates defined by the database driver")
	rootCmd.PersistentFlags().BoolP("no-back-referencing", "", false, "Disable back referencing in the loaded relationship structs")
	rootCmd.PersistentFlags().BoolP("add-global-variants", "", false, "Enable generation for global variants")
	rootCmd.PersistentFlags().BoolP("add-panic-variants", "", false, "Enable generation for panic variants")
	rootCmd.PersistentFlags().BoolP("add-soft-deletes", "", false, "Enable soft deletion by updating deleted_at timestamp")
	rootCmd.PersistentFlags().BoolP("version", "", false, "Print the version")
	rootCmd.PersistentFlags().BoolP("wipe", "", false, "Delete the output folder (rm -rf) before generation to ensure sanity")
	rootCmd.PersistentFlags().StringP("struct-tag-casing", "", "snake", "Decides the casing for go structure tag names. camel, title or snake (default snake)")
	rootCmd.PersistentFlags().StringP("relation-tag", "r", "-", "Relationship struct tag name")
	rootCmd.PersistentFlags().StringSliceP("tag-ignore", "", nil, "List of column names that should have tags values set to '-' (ignored during parsing)")

	// hide flags not recommended for use
	rootCmd.PersistentFlags().MarkHidden("replace")

	viper.BindPFlags(rootCmd.PersistentFlags())
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	viper.AutomaticEnv()

	if err := rootCmd.Execute(); err != nil {
		if e, ok := err.(commandFailure); ok {
			fmt.Printf("Error: %v\n\n", string(e))
			rootCmd.Help()
		} else if !viper.GetBool("debug") {
			fmt.Printf("Error: %v\n", err)
		} else {
			fmt.Printf("Error: %+v\n", err)
		}

		os.Exit(1)
	}
}

type commandFailure string

func (c commandFailure) Error() string {
	return string(c)
}

func preRun(cmd *cobra.Command, args []string) error {
	var err error

	if len(args) == 0 {
		return commandFailure("must provide a driver name")
	}

	driverName := args[0]
	driverPath := args[0]

	if strings.ContainsRune(driverName, os.PathSeparator) {
		driverName = strings.Replace(filepath.Base(driverName), "sqlboiler-", "", 1)
		driverName = strings.Replace(driverName, ".exe", "", 1)
	} else {
		driverPath = "sqlboiler-" + driverPath
		if p, err := exec.LookPath(driverPath); err == nil {
			driverPath = p
		}
	}

	driverPath, err = filepath.Abs(driverPath)
	if err != nil {
		return errors.Wrap(err, "could not find absolute path to driver")
	}
	drivers.RegisterBinary(driverName, driverPath)

	cmdConfig = &boilingcore.Config{
		DriverName:        driverName,
		OutFolder:         viper.GetString("output"),
		PkgName:           viper.GetString("pkgname"),
		Debug:             viper.GetBool("debug"),
		AddGlobal:         viper.GetBool("add-global-variants"),
		AddPanic:          viper.GetBool("add-panic-variants"),
		AddSoftDeletes:    viper.GetBool("add-soft-deletes"),
		NoContext:         viper.GetBool("no-context"),
		NoTests:           viper.GetBool("no-tests"),
		NoHooks:           viper.GetBool("no-hooks"),
		NoRowsAffected:    viper.GetBool("no-rows-affected"),
		NoAutoTimestamps:  viper.GetBool("no-auto-timestamps"),
		NoDriverTemplates: viper.GetBool("no-driver-templates"),
		NoBackReferencing: viper.GetBool("no-back-referencing"),
		Wipe:              viper.GetBool("wipe"),
		StructTagCasing:   strings.ToLower(viper.GetString("struct-tag-casing")), // camel | snake | title
		TagIgnore:         viper.GetStringSlice("tag-ignore"),
		RelationTag:       viper.GetString("relation-tag"),
		TemplateDirs:      viper.GetStringSlice("templates"),
		Tags:              viper.GetStringSlice("tag"),
		Replacements:      viper.GetStringSlice("replace"),
		Aliases:           boilingcore.ConvertAliases(viper.Get("aliases")),
		TypeReplaces:      boilingcore.ConvertTypeReplace(viper.Get("types")),
		Version:           sqlBoilerVersion,
	}

	if cmdConfig.Debug {
		fmt.Fprintln(os.Stderr, "using driver:", driverPath)
	}

	// Configure the driver
	cmdConfig.DriverConfig = map[string]interface{}{
		"whitelist": viper.GetStringSlice(driverName + ".whitelist"),
		"blacklist": viper.GetStringSlice(driverName + ".blacklist"),
	}

	keys := allKeys(driverName)
	for _, key := range keys {
		if key != "blacklist" && key != "whitelist" {
			prefixedKey := fmt.Sprintf("%s.%s", driverName, key)
			cmdConfig.DriverConfig[key] = viper.Get(prefixedKey)
		}
	}

	cmdConfig.Imports = configureImports()

	cmdState, err = boilingcore.New(cmdConfig)
	return err
}

func configureImports() importers.Collection {
	imports := importers.NewDefaultImports()

	mustMap := func(m importers.Map, err error) importers.Map {
		if err != nil {
			panic("failed to change viper interface into importers.Map: " + err.Error())
		}

		return m
	}

	if viper.IsSet("imports.all.standard") {
		imports.All.Standard = viper.GetStringSlice("imports.all.standard")
	}
	if viper.IsSet("imports.all.third_party") {
		imports.All.ThirdParty = viper.GetStringSlice("imports.all.third_party")
	}
	if viper.IsSet("imports.test.standard") {
		imports.Test.Standard = viper.GetStringSlice("imports.test.standard")
	}
	if viper.IsSet("imports.test.third_party") {
		imports.Test.ThirdParty = viper.GetStringSlice("imports.test.third_party")
	}
	if viper.IsSet("imports.singleton") {
		imports.Singleton = mustMap(importers.MapFromInterface(viper.Get("imports.singleton")))
	}
	if viper.IsSet("imports.test_singleton") {
		imports.TestSingleton = mustMap(importers.MapFromInterface(viper.Get("imports.test_singleton")))
	}
	if viper.IsSet("imports.based_on_type") {
		imports.BasedOnType = mustMap(importers.MapFromInterface(viper.Get("imports.based_on_type")))
	}

	return imports
}

func run(cmd *cobra.Command, args []string) error {
	return cmdState.Run()
}

func postRun(cmd *cobra.Command, args []string) error {
	return cmdState.Cleanup()
}

func allKeys(prefix string) []string {
	keys := make(map[string]bool)

	prefix += "."

	for _, e := range os.Environ() {
		splits := strings.SplitN(e, "=", 2)
		key := strings.ReplaceAll(strings.ToLower(splits[0]), "_", ".")

		if strings.HasPrefix(key, prefix) {
			keys[strings.ReplaceAll(key, prefix, "")] = true
		}
	}

	for _, key := range viper.AllKeys() {
		if strings.HasPrefix(key, prefix) {
			keys[strings.ReplaceAll(key, prefix, "")] = true
		}
	}

	keySlice := make([]string, 0, len(keys))
	for k := range keys {
		keySlice = append(keySlice, k)
	}
	return keySlice
}
<|endoftext|>


<commit_before>package main

import (
	"archive/zip"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"golang.org/x/net/context"
)

// We assume the zip file contains entries for directories too.

var progName = filepath.Base(os.Args[0])

func usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", progName)
	fmt.Fprintf(os.Stderr, "  %s ZIP MOUNTPOINT\n", progName)
	flag.PrintDefaults()
}

func main() {
	log.SetFlags(0)
	log.SetPrefix(progName + ": ")

	flag.Usage = usage
	flag.Parse()

	if flag.NArg() != 2 {
		usage()
		os.Exit(2)
	}
	path := flag.Arg(0)
	mountpoint := flag.Arg(1)
	if err := mount(path, mountpoint); err != nil {
		log.Fatal(err)
	}
}

func mount(path, mountpoint string) error {
	archive, err := zip.OpenReader(path)
	if err != nil {
		return err
	}
	defer archive.Close()

	c, err := fuse.Mount(mountpoint)
	if err != nil {
		return err
	}
	defer c.Close()

	filesys := &FS{
		archive: &archive.Reader,
	}
	if err := fs.Serve(c, filesys); err != nil {
		return err
	}

	// check if the mount process has an error to report
	<-c.Ready
	if err := c.MountError; err != nil {
		return err
	}

	return nil
}

type FS struct {
	archive *zip.Reader
}

var _ fs.FS = (*FS)(nil)

func (f *FS) Root() (fs.Node, fuse.Error) {
	n := &Dir{
		archive: f.archive,
	}
	return n, nil
}

type Dir struct {
	archive *zip.Reader
	// nil for the root directory, which has no entry in the zip
	file *zip.File
}

var _ fs.Node = (*Dir)(nil)

func zipAttr(f *zip.File) fuse.Attr {
	return fuse.Attr{
		Size:   f.UncompressedSize64,
		Mode:   f.Mode(),
		Mtime:  f.ModTime(),
		Ctime:  f.ModTime(),
		Crtime: f.ModTime(),
	}
}

func (d *Dir) Attr() fuse.Attr {
	if d.file == nil {
		// root directory
		return fuse.Attr{Mode: os.ModeDir | 0755}
	}
	return zipAttr(d.file)
}

var _ = fs.NodeRequestLookuper(&Dir{})

func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, fuse.Error) {
	path := req.Name
	if d.file != nil {
		path = d.file.Name + path
	}
	for _, f := range d.archive.File {
		switch {
		case f.Name == path:
			child := &File{
				file: f,
			}
			return child, nil
		case f.Name[:len(f.Name)-1] == path && f.Name[len(f.Name)-1] == '/':
			child := &Dir{
				archive: d.archive,
				file:    f,
			}
			return child, nil
		}
	}
	return nil, fuse.ENOENT
}

var _ = fs.HandleReadDirer(&Dir{})

func (d *Dir) ReadDir(ctx context.Context) ([]fuse.Dirent, fuse.Error) {
	prefix := ""
	if d.file != nil {
		prefix = d.file.Name
	}

	var res []fuse.Dirent
	for _, f := range d.archive.File {
		if !strings.HasPrefix(f.Name, prefix) {
			continue
		}
		name := f.Name[len(prefix):]
		if name == "" {
			// the dir itself, not a child
			continue
		}
		if strings.ContainsRune(name[:len(name)-1], '/') {
			// contains slash in the middle -> is in a deeper subdir
			continue
		}
		var de fuse.Dirent
		if name[len(name)-1] == '/' {
			// directory
			name = name[:len(name)-1]
			de.Type = fuse.DT_Dir
		}
		de.Name = name
		res = append(res, de)
	}
	return res, nil
}

type File struct {
	file *zip.File
}

var _ fs.Node = (*File)(nil)

func (f *File) Attr() fuse.Attr {
	return zipAttr(f.file)
}

var _ = fs.NodeOpener(&File{})

func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, fuse.Error) {
	r, err := f.file.Open()
	if err != nil {
		return nil, err
	}
	// individual entries inside a zip file are not seekable
	resp.Flags |= fuse.OpenNonSeekable
	return &FileHandle{r: r}, nil
}

type FileHandle struct {
	r io.ReadCloser
}

var _ fs.Handle = (*FileHandle)(nil)

var _ fs.HandleReleaser = (*FileHandle)(nil)

func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) fuse.Error {
	return fh.r.Close()
}

var _ = fs.HandleReader(&FileHandle{})

func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) fuse.Error {
	// We don't actually enforce Offset to match where previous read
	// ended. Maybe we should, but that would mean'd we need to track
	// it. The kernel *should* do it for us, based on the
	// fuse.OpenNonSeekable flag.
	buf := make([]byte, req.Size)
	n, err := fh.r.Read(buf)
	resp.Data = buf[:n]
	return err
}
<commit_msg>FUSE API change: remove fuse.Error<commit_after>package main

import (
	"archive/zip"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"golang.org/x/net/context"
)

// We assume the zip file contains entries for directories too.

var progName = filepath.Base(os.Args[0])

func usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", progName)
	fmt.Fprintf(os.Stderr, "  %s ZIP MOUNTPOINT\n", progName)
	flag.PrintDefaults()
}

func main() {
	log.SetFlags(0)
	log.SetPrefix(progName + ": ")

	flag.Usage = usage
	flag.Parse()

	if flag.NArg() != 2 {
		usage()
		os.Exit(2)
	}
	path := flag.Arg(0)
	mountpoint := flag.Arg(1)
	if err := mount(path, mountpoint); err != nil {
		log.Fatal(err)
	}
}

func mount(path, mountpoint string) error {
	archive, err := zip.OpenReader(path)
	if err != nil {
		return err
	}
	defer archive.Close()

	c, err := fuse.Mount(mountpoint)
	if err != nil {
		return err
	}
	defer c.Close()

	filesys := &FS{
		archive: &archive.Reader,
	}
	if err := fs.Serve(c, filesys); err != nil {
		return err
	}

	// check if the mount process has an error to report
	<-c.Ready
	if err := c.MountError; err != nil {
		return err
	}

	return nil
}

type FS struct {
	archive *zip.Reader
}

var _ fs.FS = (*FS)(nil)

func (f *FS) Root() (fs.Node, error) {
	n := &Dir{
		archive: f.archive,
	}
	return n, nil
}

type Dir struct {
	archive *zip.Reader
	// nil for the root directory, which has no entry in the zip
	file *zip.File
}

var _ fs.Node = (*Dir)(nil)

func zipAttr(f *zip.File) fuse.Attr {
	return fuse.Attr{
		Size:   f.UncompressedSize64,
		Mode:   f.Mode(),
		Mtime:  f.ModTime(),
		Ctime:  f.ModTime(),
		Crtime: f.ModTime(),
	}
}

func (d *Dir) Attr() fuse.Attr {
	if d.file == nil {
		// root directory
		return fuse.Attr{Mode: os.ModeDir | 0755}
	}
	return zipAttr(d.file)
}

var _ = fs.NodeRequestLookuper(&Dir{})

func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
	path := req.Name
	if d.file != nil {
		path = d.file.Name + path
	}
	for _, f := range d.archive.File {
		switch {
		case f.Name == path:
			child := &File{
				file: f,
			}
			return child, nil
		case f.Name[:len(f.Name)-1] == path && f.Name[len(f.Name)-1] == '/':
			child := &Dir{
				archive: d.archive,
				file:    f,
			}
			return child, nil
		}
	}
	return nil, fuse.ENOENT
}

var _ = fs.HandleReadDirer(&Dir{})

func (d *Dir) ReadDir(ctx context.Context) ([]fuse.Dirent, error) {
	prefix := ""
	if d.file != nil {
		prefix = d.file.Name
	}

	var res []fuse.Dirent
	for _, f := range d.archive.File {
		if !strings.HasPrefix(f.Name, prefix) {
			continue
		}
		name := f.Name[len(prefix):]
		if name == "" {
			// the dir itself, not a child
			continue
		}
		if strings.ContainsRune(name[:len(name)-1], '/') {
			// contains slash in the middle -> is in a deeper subdir
			continue
		}
		var de fuse.Dirent
		if name[len(name)-1] == '/' {
			// directory
			name = name[:len(name)-1]
			de.Type = fuse.DT_Dir
		}
		de.Name = name
		res = append(res, de)
	}
	return res, nil
}

type File struct {
	file *zip.File
}

var _ fs.Node = (*File)(nil)

func (f *File) Attr() fuse.Attr {
	return zipAttr(f.file)
}

var _ = fs.NodeOpener(&File{})

func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	r, err := f.file.Open()
	if err != nil {
		return nil, err
	}
	// individual entries inside a zip file are not seekable
	resp.Flags |= fuse.OpenNonSeekable
	return &FileHandle{r: r}, nil
}

type FileHandle struct {
	r io.ReadCloser
}

var _ fs.Handle = (*FileHandle)(nil)

var _ fs.HandleReleaser = (*FileHandle)(nil)

func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
	return fh.r.Close()
}

var _ = fs.HandleReader(&FileHandle{})

func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	// We don't actually enforce Offset to match where previous read
	// ended. Maybe we should, but that would mean'd we need to track
	// it. The kernel *should* do it for us, based on the
	// fuse.OpenNonSeekable flag.
	buf := make([]byte, req.Size)
	n, err := fh.r.Read(buf)
	resp.Data = buf[:n]
	return err
}
<|endoftext|>


<commit_before>// This file is subject to a 1-clause BSD license.
// Its contents can be found in the enclosed LICENSE file.

package main

import (
	"flag"
	"fmt"
	"github.com/jteeuwen/ircb/net"
	"github.com/jteeuwen/ircb/plugin"
	"github.com/jteeuwen/ircb/proto"
	"log"
	"os"
	"path/filepath"

	_ "github.com/jteeuwen/ircb/plugins/admin"
	_ "github.com/jteeuwen/ircb/plugins/url"
)

func main() {
	conn, client := setup()
	defer shutdown(conn, client)

	// Bind protocol handlers and commands.
	bind(client)

	// Initialize plugins.
	plugin.Load(config.Profile, client)
	defer plugin.Unload(client)

	// Perform handshake.
	log.Printf("Performing handshake...")
	client.User(config.Nickname)
	client.Nick(config.Nickname, config.NickservPassword)

	// Main data loop.
	log.Printf("Entering data loop...")
	for {
		line, err := conn.ReadLine()

		if err != nil {
			break
		}

		client.Read(string(line))
	}
}

// setup initializes the application.
func setup() (*net.Conn, *proto.Client) {
	// parse commandline arguments and create configuration.
	config = parseArgs()

	log.Printf("Connecting to %s...", config.Address)

	// Open connection to server.
	conn, err := net.Dial(config.Address, config.SSLCert, config.SSLKey)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Dial: %v\n", err)
		os.Exit(1)
	}

	log.Println("Connection established.")

	// Create client protocol.
	client := proto.NewClient(func(p []byte) error {
		_, err := conn.Write(p)
		return err
	})

	return conn, client
}

// shutdown cleans up our mess.
func shutdown(conn *net.Conn, client *proto.Client) {
	log.Printf("Shutting down.")
	client.Quit(config.QuitMessage)
	client.Close()
	conn.Close()
}

// parseArgs reads and verfies commandline arguments.
// It loads and returns a configuration object.
func parseArgs() *Config {
	profile := flag.String("p", "", "Path to bot profile directory.")
	version := flag.Bool("v", false, "Display version information.")

	flag.Parse()

	if *version {
		fmt.Printf("%s\n", Version())
		os.Exit(0)
	}

	if len(*profile) == 0 {
		fmt.Fprintf(os.Stderr, "Missing profile directory.\n")
		flag.Usage()
		os.Exit(1)
	}

	var c Config
	c.Profile = filepath.Clean(*profile)

	err := c.Load(filepath.Join(c.Profile, "config.ini"))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Load config: %v\n", err)
		os.Exit(1)
	}

	return &c
}
<commit_msg>Adds error checking for plugin initialization.<commit_after>// This file is subject to a 1-clause BSD license.
// Its contents can be found in the enclosed LICENSE file.

package main

import (
	"flag"
	"fmt"
	"github.com/jteeuwen/ircb/net"
	"github.com/jteeuwen/ircb/plugin"
	"github.com/jteeuwen/ircb/proto"
	"log"
	"os"
	"path/filepath"

	_ "github.com/jteeuwen/ircb/plugins/admin"
	_ "github.com/jteeuwen/ircb/plugins/url"
)

func main() {
	conn, client := setup()
	defer shutdown(conn, client)

	// Bind protocol handlers and commands.
	bind(client)

	// Initialize plugins.
	err := plugin.Load(config.Profile, client)
	if err != nil {
		log.Fatal(err)
	}

	defer plugin.Unload(client)

	// Perform handshake.
	log.Printf("Performing handshake...")
	client.User(config.Nickname)
	client.Nick(config.Nickname, config.NickservPassword)

	// Main data loop.
	log.Printf("Entering data loop...")
	for {
		line, err := conn.ReadLine()

		if err != nil {
			break
		}

		client.Read(string(line))
	}
}

// setup initializes the application.
func setup() (*net.Conn, *proto.Client) {
	// parse commandline arguments and create configuration.
	config = parseArgs()

	log.Printf("Connecting to %s...", config.Address)

	// Open connection to server.
	conn, err := net.Dial(config.Address, config.SSLCert, config.SSLKey)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Dial: %v\n", err)
		os.Exit(1)
	}

	log.Println("Connection established.")

	// Create client protocol.
	client := proto.NewClient(func(p []byte) error {
		_, err := conn.Write(p)
		return err
	})

	return conn, client
}

// shutdown cleans up our mess.
func shutdown(conn *net.Conn, client *proto.Client) {
	log.Printf("Shutting down.")
	client.Quit(config.QuitMessage)
	client.Close()
	conn.Close()
}

// parseArgs reads and verfies commandline arguments.
// It loads and returns a configuration object.
func parseArgs() *Config {
	profile := flag.String("p", "", "Path to bot profile directory.")
	version := flag.Bool("v", false, "Display version information.")

	flag.Parse()

	if *version {
		fmt.Printf("%s\n", Version())
		os.Exit(0)
	}

	if len(*profile) == 0 {
		fmt.Fprintf(os.Stderr, "Missing profile directory.\n")
		flag.Usage()
		os.Exit(1)
	}

	var c Config
	c.Profile = filepath.Clean(*profile)

	err := c.Load(filepath.Join(c.Profile, "config.ini"))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Load config: %v\n", err)
		os.Exit(1)
	}

	return &c
}
<|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 driver contains a Driver implementation that sends analyses to a
// CompilationAnalyzer based on a Queue of compilations.
package driver

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"

	"kythe.io/kythe/go/platform/analysis"

	apb "kythe.io/kythe/proto/analysis_proto"
)

// CompilationFunc handles a single CompilationUnit.
type CompilationFunc func(context.Context, *apb.CompilationUnit) error

// A Queue represents an ordered sequence of compilation units.
type Queue interface {
	// Next invokes f with the next available compilation in the queue.  If no
	// further values are available, Next must return io.EOF; otherwise, the
	// return value from f is propagated to the caller of Next.
	Next(_ context.Context, f CompilationFunc) error
}

// Driver sends compilations sequentially from a queue to an analyzer.
type Driver struct {
	Analyzer        analysis.CompilationAnalyzer
	FileDataService string

	// AnalysisError is called for each non-nil err returned from the Analyzer
	// (before Teardown is called).  The error returned from AnalysisError
	// replaces the analysis error that would normally be returned from Run.
	AnalysisError func(context.Context, *apb.CompilationUnit, error) error

	// Compilations is a queue of compilations to be sent for analysis.
	Compilations Queue

	// Setup is called after a compilation has been pulled from the Queue and
	// before it is sent to the Analyzer (or Output is called).
	Setup CompilationFunc
	// Output is called for each analysis output returned from the Analyzer
	Output analysis.OutputFunc
	// Teardown is called after a compilation has been analyzed and there will be no further calls to Output.
	Teardown CompilationFunc
}

func (d *Driver) validate() error {
	if d.Analyzer == nil {
		return errors.New("missing Analyzer")
	} else if d.Compilations == nil {
		return errors.New("missing Compilations Queue")
	} else if d.Output == nil {
		return errors.New("missing Output function")
	}
	return nil
}

// Run sends each compilation received from the driver's Queue to the driver's
// Analyzer.  All outputs are passed to Output in turn.  An error is immediately
// returned if the Analyzer, Output, or Compilations fields are unset.
func (d *Driver) Run(ctx context.Context) error {
	if err := d.validate(); err != nil {
		return err
	}
	for {
		if err := d.Compilations.Next(ctx, func(ctx context.Context, cu *apb.CompilationUnit) error {
			if d.Setup != nil {
				if err := d.Setup(ctx, cu); err != nil {
					return fmt.Errorf("analysis setup error: %v", err)
				}
			}
			err := d.Analyzer.Analyze(ctx, &apb.AnalysisRequest{
				Compilation:     cu,
				FileDataService: d.FileDataService,
			}, d.Output)
			if d.AnalysisError != nil && err != nil {
				err = d.AnalysisError(ctx, cu, err)
			}
			if d.Teardown != nil {
				if tErr := d.Teardown(ctx, cu); tErr != nil {
					if err == nil {
						return fmt.Errorf("analysis teardown error: %v", tErr)
					}
					log.Printf("WARNING: analysis teardown error after analysis error: %v (analysis error: %v)", tErr, err)
				}
			}
			return err
		}); err == io.EOF {
			return nil
		} else if err != nil {
			return err
		}
	}
}
<commit_msg>Allow drivers to signal an analysis retry<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 driver contains a Driver implementation that sends analyses to a
// CompilationAnalyzer based on a Queue of compilations.
package driver

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"

	"kythe.io/kythe/go/platform/analysis"

	apb "kythe.io/kythe/proto/analysis_proto"
)

// CompilationFunc handles a single CompilationUnit.
type CompilationFunc func(context.Context, *apb.CompilationUnit) error

// A Queue represents an ordered sequence of compilation units.
type Queue interface {
	// Next invokes f with the next available compilation in the queue.  If no
	// further values are available, Next must return io.EOF; otherwise, the
	// return value from f is propagated to the caller of Next.
	Next(_ context.Context, f CompilationFunc) error
}

// ErrRetry can be returned from a Driver's AnalysisError function to signal
// that the driver should retry the analysis immediately.
var ErrRetry = errors.New("retry analysis")

// Driver sends compilations sequentially from a queue to an analyzer.
type Driver struct {
	Analyzer        analysis.CompilationAnalyzer
	FileDataService string

	// Compilations is a queue of compilations to be sent for analysis.
	Compilations Queue

	// Setup is called after a compilation has been pulled from the Queue and
	// before it is sent to the Analyzer (or Output is called).
	Setup CompilationFunc
	// Output is called for each analysis output returned from the Analyzer
	Output analysis.OutputFunc
	// Teardown is called after a compilation has been analyzed and there will be no further calls to Output.
	Teardown CompilationFunc

	// AnalysisError is called for each non-nil err returned from the Analyzer
	// (before Teardown is called).  The error returned from AnalysisError
	// replaces the analysis error that would normally be returned from Run.  If
	// ErrRetry is returned, the analysis is retried immediately.
	AnalysisError func(context.Context, *apb.CompilationUnit, error) error
}

// IO is the IO subset of the analysis Driver struct.
type IO interface {
	// Setup is called after a compilation has been pulled from the Queue and
	// before it is sent to the Analyzer (or Output is called).
	Setup(context.Context, *apb.CompilationUnit) error
	// Output is called for each analysis output returned from the Analyzer
	Output(context.Context, *apb.AnalysisOutput) error
	// Teardown is called after a compilation has been analyzed and there will be no further calls to Output.
	Teardown(context.Context, *apb.CompilationUnit) error
	// AnalysisError is called for each non-nil err returned from the Analyzer
	// (before Teardown is called).  The error returned from AnalysisError
	// replaces the analysis error that would normally be returned from Run.  If
	// ErrRetry is returned, the analysis is retried immediately.
	AnalysisError(context.Context, *apb.CompilationUnit, error) error
}

// Apply updates the Driver's IO functions to be that of the given interface.
func (d *Driver) Apply(io IO) {
	d.Setup = io.Setup
	d.Output = io.Output
	d.AnalysisError = io.AnalysisError
	d.Teardown = io.Teardown
}

func (d *Driver) validate() error {
	if d.Analyzer == nil {
		return errors.New("missing Analyzer")
	} else if d.Compilations == nil {
		return errors.New("missing Compilations Queue")
	} else if d.Output == nil {
		return errors.New("missing Output function")
	}
	return nil
}

// Run sends each compilation received from the driver's Queue to the driver's
// Analyzer.  All outputs are passed to Output in turn.  An error is immediately
// returned if the Analyzer, Output, or Compilations fields are unset.
func (d *Driver) Run(ctx context.Context) error {
	if err := d.validate(); err != nil {
		return err
	}
	for {
		if err := d.Compilations.Next(ctx, func(ctx context.Context, cu *apb.CompilationUnit) error {
			if d.Setup != nil {
				if err := d.Setup(ctx, cu); err != nil {
					return fmt.Errorf("analysis setup error: %v", err)
				}
			}
			err := ErrRetry
			for err == ErrRetry {
				err = d.Analyzer.Analyze(ctx, &apb.AnalysisRequest{
					Compilation:     cu,
					FileDataService: d.FileDataService,
				}, d.Output)
				if d.AnalysisError != nil && err != nil {
					err = d.AnalysisError(ctx, cu, err)
				}
			}
			if d.Teardown != nil {
				if tErr := d.Teardown(ctx, cu); tErr != nil {
					if err == nil {
						return fmt.Errorf("analysis teardown error: %v", tErr)
					}
					log.Printf("WARNING: analysis teardown error after analysis error: %v (analysis error: %v)", tErr, err)
				}
			}
			return err
		}); err == io.EOF {
			return nil
		} else if err != nil {
			return err
		}
	}
}
<|endoftext|>


<commit_before>// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program.  If not, see <http://www.gnu.org/licenses/>.

// An example command-line tool that uses opennota/markdown to process markdown input.
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/opennota/html"
	"github.com/opennota/markdown"

	"github.com/pkg/browser"
)

var (
	allowhtml   bool
	tables      bool
	linkify     bool
	typographer bool
	xhtml       bool

	title          string
	rendererOutput string

	wg sync.WaitGroup
)

func readFromFile(fn string) ([]byte, error) {
	f, err := os.Open(fn)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	return ioutil.ReadAll(f)
}

func readFromWeb(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return ioutil.ReadAll(resp.Body)
}

func readInput(input string) ([]byte, error) {
	if strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://") {
		return readFromWeb(input)
	}
	return readFromFile(input)
}

func extractText(tok markdown.Token) string {
	switch tok := tok.(type) {
	case *markdown.Text:
		return tok.Content
	case *markdown.Inline:
		text := ""
		for _, tok := range tok.Children {
			text += extractText(tok)
		}
		return text
	}
	return ""
}

func writePreamble(w io.Writer) error {
	var opening string
	var ending string
	if xhtml {
		opening = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
`
		ending = " /"
	} else {
		opening = `<!DOCTYPE html>
`
	}
	_, err := fmt.Fprintf(w, `%s

<meta charset="utf-8"%s>
<title>%s</title>

`, opening, ending, html.EscapeString(title))

	return err
}

func writePostamble(w io.Writer) error {
	_, err := fmt.Fprint(w, `

`)
	return err
}

func handler(w http.ResponseWriter, r *http.Request) {
	defer wg.Done()

	err := writePreamble(w)
	if err != nil {
		log.Println(err)
	}

	_, err = fmt.Fprint(w, rendererOutput)
	if err != nil {
		log.Println(err)
	}

	err = writePostamble(w)
	if err != nil {
		log.Println(err)
	}

	time.Sleep(1)
}

func main() {
	log.SetFlags(log.Lshortfile)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `Usage: mdtool [options] {inputfile|URL} [outputfile]

Options:
  +h[tml]         Enable HTML
  +l[inkify]      Enable autolinking
  +ta[bles]       Enable GFM tables
  +ty[pographer]  Enable typographic replacements
  +x[html]        XHTML output

  -help           Display help

Use 'browser:' in place of the output file to get the output in a browser.
`)
	}
	flag.Parse()
	var documents []string
	for _, arg := range flag.Args() {
		switch arg {
		case "+html", "+h":
			allowhtml = true
		case "+linkify", "+l":
			linkify = true
		case "+tables", "+ta":
			tables = true
		case "+typographer", "+ty":
			typographer = true
		case "+t":
			fmt.Fprintf(os.Stderr, "ambiguous option: +t; did you mean +ta[bles] or +ty[pographer]?")
			os.Exit(1)
		case "+xhtml", "+x":
			xhtml = true
		default:
			documents = append(documents, arg)
		}
	}
	if len(documents) == 0 || len(documents) > 2 {
		flag.Usage()
		os.Exit(1)
	}

	data, err := readInput(documents[0])
	if err != nil {
		log.Fatal(err)
	}

	md := markdown.New(
		markdown.HTML(allowhtml),
		markdown.Tables(tables),
		markdown.Linkify(linkify),
		markdown.Typographer(typographer),
		markdown.XHTMLOutput(xhtml),
	)

	tokens := md.Parse(data)
	if len(tokens) > 0 {
		if heading, ok := tokens[0].(*markdown.HeadingOpen); ok {
			for i := 1; i < len(tokens); i++ {
				if tok, ok := tokens[i].(*markdown.HeadingClose); ok && tok.Lvl == heading.Lvl {
					break
				}
				title += extractText(tokens[i])
			}
		}
	}

	rendererOutput = md.RenderToString(data)

	if len(documents) == 1 {
		writePreamble(os.Stdout)
		fmt.Println(rendererOutput)
		writePostamble(os.Stdout)
	} else if documents[1] == "browser:" {
		srv := httptest.NewServer(http.HandlerFunc(handler))
		wg.Add(1)
		err = browser.OpenURL(srv.URL)
		if err != nil {
			log.Fatal(err)
		}
		wg.Wait()
	} else {
		f, err := os.OpenFile(documents[1], os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
		if err != nil {
			log.Fatal(err)
		}
		defer func() {
			err := f.Close()
			if err != nil {
				log.Println(err)
			}
		}()

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

		_, err = f.WriteString(rendererOutput)
		if err != nil {
			log.Fatal(err)
		}

		err = writePostamble(f)
		if err != nil {
			log.Fatal(err)
		}
	}
}
<commit_msg>Render data once<commit_after>// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program.  If not, see <http://www.gnu.org/licenses/>.

// An example command-line tool that uses opennota/markdown to process markdown input.
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/opennota/html"
	"github.com/opennota/markdown"

	"github.com/pkg/browser"
)

var (
	allowhtml   bool
	tables      bool
	linkify     bool
	typographer bool
	xhtml       bool

	title          string
	rendererOutput string

	wg sync.WaitGroup
)

func readFromFile(fn string) ([]byte, error) {
	f, err := os.Open(fn)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	return ioutil.ReadAll(f)
}

func readFromWeb(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return ioutil.ReadAll(resp.Body)
}

func readInput(input string) ([]byte, error) {
	if strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://") {
		return readFromWeb(input)
	}
	return readFromFile(input)
}

func extractText(tok markdown.Token) string {
	switch tok := tok.(type) {
	case *markdown.Text:
		return tok.Content
	case *markdown.Inline:
		text := ""
		for _, tok := range tok.Children {
			text += extractText(tok)
		}
		return text
	}
	return ""
}

func writePreamble(w io.Writer) error {
	var opening string
	var ending string
	if xhtml {
		opening = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
`
		ending = " /"
	} else {
		opening = `<!DOCTYPE html>
`
	}
	_, err := fmt.Fprintf(w, `%s

<meta charset="utf-8"%s>
<title>%s</title>

`, opening, ending, html.EscapeString(title))

	return err
}

func writePostamble(w io.Writer) error {
	_, err := fmt.Fprint(w, `

`)
	return err
}

func handler(w http.ResponseWriter, r *http.Request) {
	defer wg.Done()

	err := writePreamble(w)
	if err != nil {
		log.Println(err)
	}

	_, err = fmt.Fprint(w, rendererOutput)
	if err != nil {
		log.Println(err)
	}

	err = writePostamble(w)
	if err != nil {
		log.Println(err)
	}

	time.Sleep(1)
}

func main() {
	log.SetFlags(log.Lshortfile)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `Usage: mdtool [options] {inputfile|URL} [outputfile]

Options:
  +h[tml]         Enable HTML
  +l[inkify]      Enable autolinking
  +ta[bles]       Enable GFM tables
  +ty[pographer]  Enable typographic replacements
  +x[html]        XHTML output

  -help           Display help

Use 'browser:' in place of the output file to get the output in a browser.
`)
	}
	flag.Parse()
	var documents []string
	for _, arg := range flag.Args() {
		switch arg {
		case "+html", "+h":
			allowhtml = true
		case "+linkify", "+l":
			linkify = true
		case "+tables", "+ta":
			tables = true
		case "+typographer", "+ty":
			typographer = true
		case "+t":
			fmt.Fprintf(os.Stderr, "ambiguous option: +t; did you mean +ta[bles] or +ty[pographer]?")
			os.Exit(1)
		case "+xhtml", "+x":
			xhtml = true
		default:
			documents = append(documents, arg)
		}
	}
	if len(documents) == 0 || len(documents) > 2 {
		flag.Usage()
		os.Exit(1)
	}

	data, err := readInput(documents[0])
	if err != nil {
		log.Fatal(err)
	}

	md := markdown.New(
		markdown.HTML(allowhtml),
		markdown.Tables(tables),
		markdown.Linkify(linkify),
		markdown.Typographer(typographer),
		markdown.XHTMLOutput(xhtml),
	)

	tokens := md.Parse(data)
	if len(tokens) > 0 {
		if heading, ok := tokens[0].(*markdown.HeadingOpen); ok {
			for i := 1; i < len(tokens); i++ {
				if tok, ok := tokens[i].(*markdown.HeadingClose); ok && tok.Lvl == heading.Lvl {
					break
				}
				title += extractText(tokens[i])
			}
		}
	}

	rendererOutput = md.RenderTokensToString(tokens)

	if len(documents) == 1 {
		writePreamble(os.Stdout)
		fmt.Println(rendererOutput)
		writePostamble(os.Stdout)
	} else if documents[1] == "browser:" {
		srv := httptest.NewServer(http.HandlerFunc(handler))
		wg.Add(1)
		err = browser.OpenURL(srv.URL)
		if err != nil {
			log.Fatal(err)
		}
		wg.Wait()
	} else {
		f, err := os.OpenFile(documents[1], os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
		if err != nil {
			log.Fatal(err)
		}
		defer func() {
			err := f.Close()
			if err != nil {
				log.Println(err)
			}
		}()

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

		_, err = f.WriteString(rendererOutput)
		if err != nil {
			log.Fatal(err)
		}

		err = writePostamble(f)
		if err != nil {
			log.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
)

const (
	schemeHost      = "http://localhost"
	listen          = ":9154"
	uApiGet         = "/api/v1/get/"
	uApiNew         = "/api/v1/new"
	uGet            = "/g"
	uInfo           = "/i"
	uFav            = "/favicon.ico"
	uCss            = "/custom.css"
	maxData         = 1048576 // 1MB
	defaultValidity = 7       // days
	expiryCheck     = 30      // minutes
)

func Log(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s \"%s %s %s\" \"%s\"", r.RemoteAddr, r.Method, r.URL.Path, r.Proto, r.Header.Get("User-Agent"))
		handler.ServeHTTP(w, r)
	})
}

func main() {
	store := make(secretStore)
	store.NewEntry("secret", 100, 0, "_authtoken_", "test")
	go store.Expiry()

	auth := makeTokenDB()

	tView := template.New("view")
	tView.Parse(htmlMaster)
	tView.Parse(htmlView)
	tViewErr := template.New("viewErr")
	tViewErr.Parse(htmlMaster)
	tViewErr.Parse(htmlViewErr)
	tViewInfo := template.New("viewInfo")
	tViewInfo.Parse(htmlMaster)
	tViewInfo.Parse(htmlViewInfo)

	http.HandleFunc(uApiGet, func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Path[len(uApiGet):]
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		if entry, ok := store.GetEntryInfo(id); !ok {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintln(w, "{}")
		} else {
			store.Click(id)
			w.WriteHeader(http.StatusOK)
			if err := json.NewEncoder(w).Encode(entry); err != nil {
				panic(err)
			}
		}
	})

	http.HandleFunc(uApiNew, func(w http.ResponseWriter, r *http.Request) {
		var entry StoreEntry
		body, err := ioutil.ReadAll(io.LimitReader(r.Body, maxData))
		if err != nil {
			panic(err)
		}
		if err := r.Body.Close(); err != nil {
			panic(err)
		}
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		if err := json.Unmarshal(body, &entry); err != nil {
			w.WriteHeader(422) // unprocessable entity
			if err := json.NewEncoder(w).Encode(err); err != nil {
				panic(err)
			}
		}
		if !auth.isAuthorized(&entry) {
			w.WriteHeader(http.StatusUnauthorized)
			fmt.Fprintln(w, `{"error":"unauthorized"}`)
		} else {
			id := store.AddEntry(entry, "")
			newEntry, _ := store.GetEntryInfoHidden(id)
			log.Println("New ID:", id)
			w.WriteHeader(http.StatusCreated)
			if err := json.NewEncoder(w).Encode(newEntry); err != nil {
				panic(err)
			}
		}
	})

	http.HandleFunc(uGet, func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Query().Get("id")
		if entry, ok := store.GetEntryInfo(id); !ok {
			w.WriteHeader(http.StatusNotFound)
			tViewErr.ExecuteTemplate(w, "master", nil)
		} else {
			store.Click(id)
			w.WriteHeader(http.StatusOK)
			tView.ExecuteTemplate(w, "master", entry)
		}
	})

	http.HandleFunc(uInfo, func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Query().Get("id")
		if entry, ok := store.GetEntryInfo(id); !ok {
			w.WriteHeader(http.StatusNotFound)
			tViewErr.ExecuteTemplate(w, "master", nil)
		} else {
			w.WriteHeader(http.StatusOK)
			tViewInfo.ExecuteTemplate(w, "master", entry)
		}
	})

	http.HandleFunc(uFav, func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "image/x-icon")
		w.WriteHeader(http.StatusOK)
		w.Write(favicon)
	})

	http.HandleFunc(uCss, func(w http.ResponseWriter, r *http.Request) {
		css := tryReadFile(cssFileName)
		w.Header().Set("Content-Type", "text/css")
		w.WriteHeader(http.StatusOK)
		w.Write(css)
	})

	log.Fatal(http.ListenAndServe(listen, Log(http.DefaultServeMux)))
}
<commit_msg>read css and authdb on startup and on SIGHUP<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
)

const (
	schemeHost      = "http://localhost"
	listen          = ":9154"
	uApiGet         = "/api/v1/get/"
	uApiNew         = "/api/v1/new"
	uGet            = "/g"
	uInfo           = "/i"
	uFav            = "/favicon.ico"
	uCss            = "/custom.css"
	maxData         = 1048576 // 1MB
	defaultValidity = 7       // days
	expiryCheck     = 30      // minutes
)

var (
	auth TokenDB
	css  []byte
)

func Log(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s \"%s %s %s\" \"%s\"", r.RemoteAddr, r.Method, r.URL.Path, r.Proto, r.Header.Get("User-Agent"))
		handler.ServeHTTP(w, r)
	})
}

func updateFiles() {
	auth = makeTokenDB()
	css = tryReadFile(cssFileName)
}

func main() {
	store := make(secretStore)
	store.NewEntry("secret", 100, 0, "_authtoken_", "test")
	go store.Expiry()

	updateFiles()

	sighup := make(chan os.Signal, 1)
	signal.Notify(sighup, syscall.SIGHUP)
	go func() {
		for {
			<-sighup
			log.Println("reloading configuration...")
			updateFiles()
		}
	}()

	tView := template.New("view")
	tView.Parse(htmlMaster)
	tView.Parse(htmlView)
	tViewErr := template.New("viewErr")
	tViewErr.Parse(htmlMaster)
	tViewErr.Parse(htmlViewErr)
	tViewInfo := template.New("viewInfo")
	tViewInfo.Parse(htmlMaster)
	tViewInfo.Parse(htmlViewInfo)

	http.HandleFunc(uApiGet, func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Path[len(uApiGet):]
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		if entry, ok := store.GetEntryInfo(id); !ok {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintln(w, "{}")
		} else {
			store.Click(id)
			w.WriteHeader(http.StatusOK)
			if err := json.NewEncoder(w).Encode(entry); err != nil {
				panic(err)
			}
		}
	})

	http.HandleFunc(uApiNew, func(w http.ResponseWriter, r *http.Request) {
		var entry StoreEntry
		body, err := ioutil.ReadAll(io.LimitReader(r.Body, maxData))
		if err != nil {
			panic(err)
		}
		if err := r.Body.Close(); err != nil {
			panic(err)
		}
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		if err := json.Unmarshal(body, &entry); err != nil {
			w.WriteHeader(422) // unprocessable entity
			if err := json.NewEncoder(w).Encode(err); err != nil {
				panic(err)
			}
		}
		if !auth.isAuthorized(&entry) {
			w.WriteHeader(http.StatusUnauthorized)
			fmt.Fprintln(w, `{"error":"unauthorized"}`)
		} else {
			id := store.AddEntry(entry, "")
			newEntry, _ := store.GetEntryInfoHidden(id)
			log.Println("New ID:", id)
			w.WriteHeader(http.StatusCreated)
			if err := json.NewEncoder(w).Encode(newEntry); err != nil {
				panic(err)
			}
		}
	})

	http.HandleFunc(uGet, func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Query().Get("id")
		if entry, ok := store.GetEntryInfo(id); !ok {
			w.WriteHeader(http.StatusNotFound)
			tViewErr.ExecuteTemplate(w, "master", nil)
		} else {
			store.Click(id)
			w.WriteHeader(http.StatusOK)
			tView.ExecuteTemplate(w, "master", entry)
		}
	})

	http.HandleFunc(uInfo, func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Query().Get("id")
		if entry, ok := store.GetEntryInfo(id); !ok {
			w.WriteHeader(http.StatusNotFound)
			tViewErr.ExecuteTemplate(w, "master", nil)
		} else {
			w.WriteHeader(http.StatusOK)
			tViewInfo.ExecuteTemplate(w, "master", entry)
		}
	})

	http.HandleFunc(uFav, func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "image/x-icon")
		w.WriteHeader(http.StatusOK)
		w.Write(favicon)
	})

	http.HandleFunc(uCss, func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/css")
		w.WriteHeader(http.StatusOK)
		w.Write(css)
	})

	log.Fatal(http.ListenAndServe(listen, Log(http.DefaultServeMux)))
}
<|endoftext|>