<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path"
	"time"

	"github.com/gorilla/mux"
	_ "github.com/mattn/go-sqlite3"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

var DB *sql.DB

type Item struct {
	ID          string `json:"id"`
	Description string `json:"description"`
	User        string `json:"user,omitempty"`
}

func addHeaders(rw http.ResponseWriter) {
	rw.Header().Add("Access-Control-Allow-Origin", "http://localhost:8080")
	rw.Header().Add("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS")
	rw.Header().Add("Access-Control-Allow-Headers", "Content-Type")
	rw.Header().Add("Access-Control-Allow-Credentials", "true")
	rw.Header().Add("Content-Type", "application/json")
}

func GetHandler(rw http.ResponseWriter, req *http.Request) {
	addHeaders(rw)
	if req.Method == "OPTIONS" {
		return
	}

	tx, err := DB.Begin()
	if err != nil {
		log.Printf("Error begining transaction: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		return
	}
	defer tx.Rollback()

	cur, err := tx.Query("SELECT * FROM items")
	if err != nil {
		log.Printf("Error retrieving list: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		return
	}

	var items []*Item

	for cur.Next() {
		item := &Item{}
		if err := cur.Scan(&item.ID, &item.Description, &item.User); err != nil {
			log.Printf("Error retrieving item: %v", err)
			rw.WriteHeader(500)
			rw.Write([]byte("oops"))
			return
		}
		items = append(items, item)
	}

	resp := &struct {
		List []*Item `json:"list"`
	}{List: items}
	body, err := json.Marshal(resp)
	if err != nil {
		log.Printf("Error marshalling items: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		return
	}
	rw.WriteHeader(200)
	rw.Write(body)
}

func AddHandler(rw http.ResponseWriter, req *http.Request) {
	addHeaders(rw)
	if req.Method == "OPTIONS" {
		return
	}

	tx, err := DB.Begin()
	if err != nil {
		log.Printf("Error begining transaction: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		return
	}

	item := &Item{}
	if err := json.NewDecoder(req.Body).Decode(item); err != nil {
		log.Printf("Error decoding request: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		tx.Rollback()
		return
	}

	item.ID = fmt.Sprintf("%x", rand.Int63())
	_, err = tx.Exec(
		"INSERT INTO items (id, description) VALUES (?, ?)",
		item.ID, item.Description,
	)
	if err != nil {
		log.Printf("Error inserting item: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		tx.Rollback()
		return
	}

	if err := tx.Commit(); err != nil {
		log.Printf("Error committing item: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		tx.Rollback()
		return
	}
	req.URL.RawQuery = ""
	req.URL.Fragment = ""
	rw.Header().Add("Location", req.URL.String()+"/"+item.ID)
	rw.WriteHeader(201)
	json.NewEncoder(rw).Encode(item)
}

func UpdateHandler(rw http.ResponseWriter, req *http.Request) {
	addHeaders(rw)
	if req.Method == "OPTIONS" {
		return
	}

	tx, err := DB.Begin()
	if err != nil {
		log.Printf("Error begining transaction: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		return
	}

	newitem := &Item{}
	if err := json.NewDecoder(req.Body).Decode(newitem); err != nil {
		log.Printf("Error decoding request: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		tx.Rollback()
		return
	}
	vars := mux.Vars(req)
	newitem.ID = vars["id"]
	if newitem.ID == "" {
		log.Printf("Empty ID: %v", vars)
		rw.WriteHeader(400)
		rw.Write([]byte("missing id id"))
		tx.Rollback()
		return
	}

	{
		olditem := &Item{ID: "id"}
		row := tx.QueryRow("SELECT * FROM items WHERE id = ? LIMIT 1", newitem.ID)
		if err := row.Scan(&olditem.ID, &olditem.Description, &olditem.User); err != nil {
			log.Printf("Error querying item: %v", err)
			rw.WriteHeader(500)
			rw.Write([]byte("oops"))
			tx.Rollback()
			return
		}
		newitem.Description = olditem.Description
	}

	_, err = tx.Exec(
		"UPDATE items SET description = ?, user = ? WHERE id = ?",
		newitem.Description, newitem.User, newitem.ID,
	)
	if err != nil {
		log.Printf("Error updating item: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		tx.Rollback()
		return
	}

	if err := tx.Commit(); err != nil {
		log.Printf("Error committing item: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		tx.Rollback()
		return
	}
	req.URL.RawQuery = ""
	req.URL.Fragment = ""
	rw.Header().Add("Location", req.URL.String()+"/"+newitem.ID)
	rw.WriteHeader(201)
	json.NewEncoder(rw).Encode(newitem)
}

func createTable(table, create string) {
	create = fmt.Sprintf(create, table)

	tx, err := DB.Begin()
	if err != nil {
		log.Fatalf("Error beginning transaction: %v", err)
	}

	row := tx.QueryRow("SELECT COUNT(1) FROM sqlite_master WHERE type = ? AND name = ?", "table", table)
	count := 0
	if err := row.Scan(&count); err != nil {
		log.Fatalf("Error reading results of '%s' table check: %v", table, err)
	}

	if count > 0 {
		tx.Rollback()
		return
	}
	if _, err := tx.Exec(create); err != nil {
		log.Fatalf("Error creating table %s: %v", table, err)
	}
	if err := tx.Commit(); err != nil {
		log.Fatalf("Error commiting table creation %s: %v", table, err)
	}
}

func main() {
	log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
	bind := flag.String("bind", "localhost:8000", "host:port to listen on")
	dbFile := flag.String("db", "signup.sqlite3", "filename for db")
	staticPath := flag.String("path", "www", "path to static www assests")
	flag.Parse()

	router, err := setup(*staticPath, *dbFile)
	if err != nil {
		log.Fatalf("Error starting up: %v", err)
	}
	m := http.NewServeMux()
	m.Handle("/", router)

	log.Printf("Starting %s on http://%s", path.Base(os.Args[0]), *bind)
	log.Fatal(http.ListenAndServe(*bind, m))
}

func initDB() {
	createTable("items", "CREATE TABLE %s ( id TEXT PRIMARY KEY, description TEXT NOT NULL DEFAULT '', user TEXT NOT NULL DEFAULT '' )")
}

func setup(staticPath, dbFile string) (*mux.Router, error) {
	var err error
	if DB, err = sql.Open("sqlite3", dbFile); err != nil {
		return nil, fmt.Errorf("Error opening database: %v", err)
	}

	initDB()

	r := mux.NewRouter()
	r.HandleFunc("/api/list", GetHandler).Methods("GET", "OPTIONS")
	r.HandleFunc("/api/list", AddHandler).Methods("POST", "OPTIONS")
	r.HandleFunc("/api/list/{id}", UpdateHandler).Methods("POST", "OPTIONS")
	r.PathPrefix("/").Handler(http.FileServer(http.Dir(staticPath)))

	return r, nil
}
<commit_msg>@pietvanzoen hates null<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path"
	"time"

	"github.com/gorilla/mux"
	_ "github.com/mattn/go-sqlite3"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

var DB *sql.DB

type Item struct {
	ID          string `json:"id"`
	Description string `json:"description"`
	User        string `json:"user,omitempty"`
}

func addHeaders(rw http.ResponseWriter) {
	rw.Header().Add("Access-Control-Allow-Origin", "http://localhost:8080")
	rw.Header().Add("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS")
	rw.Header().Add("Access-Control-Allow-Headers", "Content-Type")
	rw.Header().Add("Access-Control-Allow-Credentials", "true")
	rw.Header().Add("Content-Type", "application/json")
}

func GetHandler(rw http.ResponseWriter, req *http.Request) {
	addHeaders(rw)
	if req.Method == "OPTIONS" {
		return
	}

	tx, err := DB.Begin()
	if err != nil {
		log.Printf("Error begining transaction: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		return
	}
	defer tx.Rollback()

	cur, err := tx.Query("SELECT * FROM items")
	if err != nil {
		log.Printf("Error retrieving list: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		return
	}

	items := []*Item{}

	for cur.Next() {
		item := &Item{}
		if err := cur.Scan(&item.ID, &item.Description, &item.User); err != nil {
			log.Printf("Error retrieving item: %v", err)
			rw.WriteHeader(500)
			rw.Write([]byte("oops"))
			return
		}
		items = append(items, item)
	}

	resp := &struct {
		List []*Item `json:"list"`
	}{List: items}
	body, err := json.Marshal(resp)
	if err != nil {
		log.Printf("Error marshalling items: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		return
	}
	rw.WriteHeader(200)
	rw.Write(body)
}

func AddHandler(rw http.ResponseWriter, req *http.Request) {
	addHeaders(rw)
	if req.Method == "OPTIONS" {
		return
	}

	tx, err := DB.Begin()
	if err != nil {
		log.Printf("Error begining transaction: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		return
	}

	item := &Item{}
	if err := json.NewDecoder(req.Body).Decode(item); err != nil {
		log.Printf("Error decoding request: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		tx.Rollback()
		return
	}

	item.ID = fmt.Sprintf("%x", rand.Int63())
	_, err = tx.Exec(
		"INSERT INTO items (id, description) VALUES (?, ?)",
		item.ID, item.Description,
	)
	if err != nil {
		log.Printf("Error inserting item: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		tx.Rollback()
		return
	}

	if err := tx.Commit(); err != nil {
		log.Printf("Error committing item: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		tx.Rollback()
		return
	}
	req.URL.RawQuery = ""
	req.URL.Fragment = ""
	rw.Header().Add("Location", req.URL.String()+"/"+item.ID)
	rw.WriteHeader(201)
	json.NewEncoder(rw).Encode(item)
}

func UpdateHandler(rw http.ResponseWriter, req *http.Request) {
	addHeaders(rw)
	if req.Method == "OPTIONS" {
		return
	}

	tx, err := DB.Begin()
	if err != nil {
		log.Printf("Error begining transaction: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		return
	}

	newitem := &Item{}
	if err := json.NewDecoder(req.Body).Decode(newitem); err != nil {
		log.Printf("Error decoding request: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		tx.Rollback()
		return
	}
	vars := mux.Vars(req)
	newitem.ID = vars["id"]
	if newitem.ID == "" {
		log.Printf("Empty ID: %v", vars)
		rw.WriteHeader(400)
		rw.Write([]byte("missing id id"))
		tx.Rollback()
		return
	}

	{
		olditem := &Item{ID: "id"}
		row := tx.QueryRow("SELECT * FROM items WHERE id = ? LIMIT 1", newitem.ID)
		if err := row.Scan(&olditem.ID, &olditem.Description, &olditem.User); err != nil {
			log.Printf("Error querying item: %v", err)
			rw.WriteHeader(500)
			rw.Write([]byte("oops"))
			tx.Rollback()
			return
		}
		newitem.Description = olditem.Description
	}

	_, err = tx.Exec(
		"UPDATE items SET description = ?, user = ? WHERE id = ?",
		newitem.Description, newitem.User, newitem.ID,
	)
	if err != nil {
		log.Printf("Error updating item: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		tx.Rollback()
		return
	}

	if err := tx.Commit(); err != nil {
		log.Printf("Error committing item: %v", err)
		rw.WriteHeader(500)
		rw.Write([]byte("oops"))
		tx.Rollback()
		return
	}
	req.URL.RawQuery = ""
	req.URL.Fragment = ""
	rw.Header().Add("Location", req.URL.String()+"/"+newitem.ID)
	rw.WriteHeader(201)
	json.NewEncoder(rw).Encode(newitem)
}

func createTable(table, create string) {
	create = fmt.Sprintf(create, table)

	tx, err := DB.Begin()
	if err != nil {
		log.Fatalf("Error beginning transaction: %v", err)
	}

	row := tx.QueryRow("SELECT COUNT(1) FROM sqlite_master WHERE type = ? AND name = ?", "table", table)
	count := 0
	if err := row.Scan(&count); err != nil {
		log.Fatalf("Error reading results of '%s' table check: %v", table, err)
	}

	if count > 0 {
		tx.Rollback()
		return
	}
	if _, err := tx.Exec(create); err != nil {
		log.Fatalf("Error creating table %s: %v", table, err)
	}
	if err := tx.Commit(); err != nil {
		log.Fatalf("Error commiting table creation %s: %v", table, err)
	}
}

func main() {
	log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
	bind := flag.String("bind", "localhost:8000", "host:port to listen on")
	dbFile := flag.String("db", "signup.sqlite3", "filename for db")
	staticPath := flag.String("path", "www", "path to static www assests")
	flag.Parse()

	router, err := setup(*staticPath, *dbFile)
	if err != nil {
		log.Fatalf("Error starting up: %v", err)
	}
	m := http.NewServeMux()
	m.Handle("/", router)

	log.Printf("Starting %s on http://%s", path.Base(os.Args[0]), *bind)
	log.Fatal(http.ListenAndServe(*bind, m))
}

func initDB() {
	createTable("items", "CREATE TABLE %s ( id TEXT PRIMARY KEY, description TEXT NOT NULL DEFAULT '', user TEXT NOT NULL DEFAULT '' )")
}

func setup(staticPath, dbFile string) (*mux.Router, error) {
	var err error
	if DB, err = sql.Open("sqlite3", dbFile); err != nil {
		return nil, fmt.Errorf("Error opening database: %v", err)
	}

	initDB()

	r := mux.NewRouter()
	r.HandleFunc("/api/list", GetHandler).Methods("GET", "OPTIONS")
	r.HandleFunc("/api/list", AddHandler).Methods("POST", "OPTIONS")
	r.HandleFunc("/api/list/{id}", UpdateHandler).Methods("POST", "OPTIONS")
	r.PathPrefix("/").Handler(http.FileServer(http.Dir(staticPath)))

	return r, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The mccdaq developers. All rights reserved.
// Project site: https://github.com/gotmc/mccdaq
// Use of this source code is governed by a MIT-style license that
// can be found in the LICENSE.txt file for the project.

package main

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"time"

	"github.com/gizak/termui"
	"github.com/gotmc/libusb"
	"github.com/gotmc/mccdaq/usb1608fsplus"
)

const (
	millisecondDelay = 100
	termWidth        = 70
)

func main() {

	// Initialize the USB Context
	ctx, err := libusb.Init()
	if err != nil {
		log.Fatal("Couldn't create USB context. Ending now.")
	}
	defer ctx.Exit()

	// Find the first USB fevice with the VendorID and ProductID matching the MCC
	// USB-1608FS-Plus DAQ
	daq, err := usb1608fsplus.GetFirstDevice(ctx)
	if err != nil {
		log.Fatalf("Couldn't find a USB-1608FS-Plus: %s", err)
	}
	defer daq.Close()

	// Read the calibration memory to setup the gain table
	// gainTable, _ := daq.BuildGainTable()

	// Create new analog input and ensure the scan is stopped and buffer cleared
	ai, err := daq.NewAnalogInput()
	if err != nil {
		log.Fatalf("Error creating new analog input: %s", err)
	}
	ai.StopScan()
	time.Sleep(millisecondDelay * time.Millisecond)
	ai.ClearScanBuffer()

	// Initialize the terminal UI
	err = termui.Init()
	if err != nil {
		panic(err)
	}
	defer termui.Close()

	//termui.UseTheme("helloworld")

	// Setup list of info
	var infoStrings = make([]string, 5)
	serialNumber, err := daq.SerialNumber()
	if err != nil {
		serialNumber = "Unknown"
	}
	infoStrings[0] = fmt.Sprintf("S/N %s", serialNumber)
	infoList := termui.NewList()
	infoList.Items = infoStrings
	infoList.ItemFgColor = termui.ColorYellow
	infoList.BorderLabel = "USB-1608FS-Plus Info"
	infoList.Height = len(infoStrings) + 2
	infoList.Width = termWidth
	infoList.Y = 0

	var strs = make([]string, 6)

	ls := termui.NewList()
	ls.Items = strs
	ls.ItemFgColor = termui.ColorYellow
	ls.BorderLabel = "Analog Inputs"
	ls.Height = len(strs) + 2
	ls.Width = termWidth
	ls.Y = infoList.Y + infoList.Height

	par0 := termui.NewPar("Press q to quit")
	par0.Height = 1
	par0.Width = termWidth
	par0.Y = ls.Y + ls.Height
	par0.Border = false

	termui.Handle("/sys/kbd/q", func(termui.Event) {
		termui.StopLoop()
	})

	/**************************
	* Start the Analog Scan   *
	**************************/

	// Setup the analog input scan
	configData, err := ioutil.ReadFile("./analog_config.json")
	if err != nil {
		log.Fatalf("Error reading the USB-1608FS-Plus JSON config file")
	}
	dec := json.NewDecoder(bytes.NewReader(configData))
	var configJSON = struct {
		ScansPerBuffer             int `json:"scans_per_buffer"`
		TotalBuffers               int `json:"total_buffers"`
		*usb1608fsplus.AnalogInput `json:"analog_input"`
	}{
		0,
		0,
		ai,
	}
	if err := dec.Decode(&configJSON); err != nil {
		log.Fatalf("parse USB-1608FS-Plus: %v", err)
	}
	scansPerBuffer := configJSON.ScansPerBuffer
	totalBuffers := configJSON.TotalBuffers
	ai.SetScanRanges()
	infoStrings[1] = fmt.Sprintf("Scans/buffer = %d", scansPerBuffer)
	infoStrings[2] = fmt.Sprintf("Total buffers = %d", totalBuffers)

	// Read the scan ranges
	time.Sleep(millisecondDelay * time.Millisecond)
	_, err = ai.ScanRanges()

	// Read the totalScans using splitScansIn number of scans
	ai.StartScan(0)
	totalBytesRead := 0
	for j := 0; j < totalBuffers; j++ {
		// time.Sleep(millisecondDelay * time.Millisecond)
		data, err := ai.ReadScan(scansPerBuffer)
		totalBytesRead += len(data)
		if err != nil {
			// Stop the analog scan and close the DAQ
			ai.StopScan()
			time.Sleep(millisecondDelay * time.Millisecond)
			daq.Close()
			log.Fatalf("Error reading scan: %s", err)
		}
		// Show the first value measured for each channel
		wordsToShow := 6
		for i := 0; i < wordsToShow; i++ {
			desiredByte := i * 2
			ch := ai.Channels[i]
			volts, err := ch.Volts(data[desiredByte : desiredByte+2])
			encodedValue := int(binary.LittleEndian.Uint16(data[desiredByte : desiredByte+2]))
			if err != nil {
				strs[i] = fmt.Sprintf("%5s = %d (Error: %s)\n",
					ch.Description, encodedValue, err)
			} else {
				strs[i] = fmt.Sprintf("[%6s](fg-red) = [%.5f V](fg-white) (%d / %#x) @ %srange\n",
					ch.Description, volts, encodedValue, encodedValue, ch.Range)
			}
		}
		infoStrings[4] = fmt.Sprintf("Frequency = %f Hz", ai.Frequency)
		infoStrings[3] = fmt.Sprintf("Bytes read = %d\n", totalBytesRead)
		termui.Render(infoList, ls, par0)
	}

	// Stop the analog scan and close the DAQ
	time.Sleep(millisecondDelay * time.Millisecond)
	ai.StopScan()
	time.Sleep(millisecondDelay * time.Millisecond)
	termui.Loop()
}
<commit_msg>Pull GPIO3 low if running on RPi<commit_after>// Copyright (c) 2016 The mccdaq developers. All rights reserved.
// Project site: https://github.com/gotmc/mccdaq
// Use of this source code is governed by a MIT-style license that
// can be found in the LICENSE.txt file for the project.

package main

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"runtime"
	"time"

	"github.com/gizak/termui"
	"github.com/gotmc/libusb"
	"github.com/gotmc/mccdaq/usb1608fsplus"
	rpi "github.com/nathan-osman/go-rpigpio"
)

const (
	millisecondDelay = 100
	termWidth        = 70
)

func main() {

	// If running from RPi, set GPIO3 low.
	if runtime.GOARCH == "arm" {
		gpio3, err := rpi.OpenPin(3, rpi.OUT)
		if err != nil {
			panic(err)
		}
		defer gpio3.Close()
		gpio3.Write(rpi.LOW)
	}

	// Initialize the USB Context
	ctx, err := libusb.Init()
	if err != nil {
		log.Fatal("Couldn't create USB context. Ending now.")
	}
	defer ctx.Exit()

	// Find the first USB fevice with the VendorID and ProductID matching the MCC
	// USB-1608FS-Plus DAQ
	daq, err := usb1608fsplus.GetFirstDevice(ctx)
	if err != nil {
		log.Fatalf("Couldn't find a USB-1608FS-Plus: %s", err)
	}
	defer daq.Close()

	// Read the calibration memory to setup the gain table
	// gainTable, _ := daq.BuildGainTable()

	// Create new analog input and ensure the scan is stopped and buffer cleared
	ai, err := daq.NewAnalogInput()
	if err != nil {
		log.Fatalf("Error creating new analog input: %s", err)
	}
	ai.StopScan()
	time.Sleep(millisecondDelay * time.Millisecond)
	ai.ClearScanBuffer()

	// Initialize the terminal UI
	err = termui.Init()
	if err != nil {
		panic(err)
	}
	defer termui.Close()

	//termui.UseTheme("helloworld")

	// Setup list of info
	var infoStrings = make([]string, 5)
	serialNumber, err := daq.SerialNumber()
	if err != nil {
		serialNumber = "Unknown"
	}
	infoStrings[0] = fmt.Sprintf("S/N %s", serialNumber)
	infoList := termui.NewList()
	infoList.Items = infoStrings
	infoList.ItemFgColor = termui.ColorYellow
	infoList.BorderLabel = "USB-1608FS-Plus Info"
	infoList.Height = len(infoStrings) + 2
	infoList.Width = termWidth
	infoList.Y = 0

	var strs = make([]string, 6)

	ls := termui.NewList()
	ls.Items = strs
	ls.ItemFgColor = termui.ColorYellow
	ls.BorderLabel = "Analog Inputs"
	ls.Height = len(strs) + 2
	ls.Width = termWidth
	ls.Y = infoList.Y + infoList.Height

	par0 := termui.NewPar("Press q to quit")
	par0.Height = 1
	par0.Width = termWidth
	par0.Y = ls.Y + ls.Height
	par0.Border = false

	termui.Handle("/sys/kbd/q", func(termui.Event) {
		termui.StopLoop()
	})

	/**************************
	* Start the Analog Scan   *
	**************************/

	// Setup the analog input scan
	configData, err := ioutil.ReadFile("./analog_config.json")
	if err != nil {
		log.Fatalf("Error reading the USB-1608FS-Plus JSON config file")
	}
	dec := json.NewDecoder(bytes.NewReader(configData))
	var configJSON = struct {
		ScansPerBuffer             int `json:"scans_per_buffer"`
		TotalBuffers               int `json:"total_buffers"`
		*usb1608fsplus.AnalogInput `json:"analog_input"`
	}{
		0,
		0,
		ai,
	}
	if err := dec.Decode(&configJSON); err != nil {
		log.Fatalf("parse USB-1608FS-Plus: %v", err)
	}
	scansPerBuffer := configJSON.ScansPerBuffer
	totalBuffers := configJSON.TotalBuffers
	ai.SetScanRanges()
	infoStrings[1] = fmt.Sprintf("Scans/buffer = %d", scansPerBuffer)
	infoStrings[2] = fmt.Sprintf("Total buffers = %d", totalBuffers)

	// Read the scan ranges
	time.Sleep(millisecondDelay * time.Millisecond)
	_, err = ai.ScanRanges()

	// Read the totalScans using splitScansIn number of scans
	ai.StartScan(0)
	totalBytesRead := 0
	for j := 0; j < totalBuffers; j++ {
		// time.Sleep(millisecondDelay * time.Millisecond)
		data, err := ai.ReadScan(scansPerBuffer)
		totalBytesRead += len(data)
		if err != nil {
			// Stop the analog scan and close the DAQ
			ai.StopScan()
			time.Sleep(millisecondDelay * time.Millisecond)
			daq.Close()
			log.Fatalf("Error reading scan: %s", err)
		}
		// Show the first value measured for each channel
		wordsToShow := 6
		for i := 0; i < wordsToShow; i++ {
			desiredByte := i * 2
			ch := ai.Channels[i]
			volts, err := ch.Volts(data[desiredByte : desiredByte+2])
			encodedValue := int(binary.LittleEndian.Uint16(data[desiredByte : desiredByte+2]))
			if err != nil {
				strs[i] = fmt.Sprintf("%5s = %d (Error: %s)\n",
					ch.Description, encodedValue, err)
			} else {
				strs[i] = fmt.Sprintf("[%6s](fg-red) = [%.5f V](fg-white) (%d / %#x) @ %srange\n",
					ch.Description, volts, encodedValue, encodedValue, ch.Range)
			}
		}
		infoStrings[4] = fmt.Sprintf("Frequency = %f Hz", ai.Frequency)
		infoStrings[3] = fmt.Sprintf("Bytes read = %d\n", totalBytesRead)
		termui.Render(infoList, ls, par0)
	}

	// Stop the analog scan and close the DAQ
	time.Sleep(millisecondDelay * time.Millisecond)
	ai.StopScan()
	time.Sleep(millisecondDelay * time.Millisecond)
	termui.Loop()
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
	_ "github.com/go-sql-driver/mysql"
	mailgun "github.com/riobard/go-mailgun"
)

type User struct {
	Id int
	username string
	Email string
}

type GithubUser struct {
	Id int
	Login string
	Url string
}

// db thing.
type GithubRepo struct {
	Id int
	User string // should probably be Login
	RepoName string // should probably be Name
}

func (g *GithubRepo) FullName() string {
	return fmt.Sprintf("%s/%s", g.User, g.RepoName)
}

type GithubApiRepo struct {
	Id int
	Owner GithubUser
	Name NString
	Description NString
}

func (g *GithubApiRepo) FullName() string {
	return fmt.Sprintf("%s/%s", g.Owner.Login, g.Name)
}

type Activity struct {
	Id int
	github_id int
	activity_type string // should be enum
	created_at time.Time
	Username string
	repo GithubRepo // this isn't going to be memory efficient
	Meta string // full payload of json object
}

type Treeish struct {
	Label string
	Sha string
	Repo GithubApiRepo
	Html_url string
	User GithubUser
}

type Commit struct {
	Sha string
	Message string
	Author struct {
		Name string
		Email string
	}
}

func (c *Commit) ShortSha() string {
	return c.Sha[0:6]
}

func (c *Commit) ShortCommit() string {
	msg := strings.Split(c.Message, "\n")[0]
	if len(msg) > 80 {
		return msg[0:77] + "..."
	}
	return msg
}

// Nullable String
type NString string

func (n *NString) UnmarshalJSON(b []byte) (err error) {
	if string(b) == "null" {
		return nil
	}
	return json.Unmarshal(b, (*string)(n))
}

func get_users_repos(db *sql.DB, user_id int) ([]GithubRepo, error) {
	var repo_count int

	// TODO(justinabrahms): Should really alter the schema such
	// that the join from user <-> repo doesn't go through
	// userprofiles.
	row := db.QueryRow("SELECT count(*) FROM streamer_repo r" +
	" JOIN streamer_userprofile_repos upr ON upr.repo_id = r.id " +
	" JOIN streamer_userprofile up ON up.id = upr.userprofile_id" +
	" WHERE up.user_id = ?;", user_id)

	err := row.Scan(&repo_count)
	if (err != nil) {
		return nil, err
	}
	var repos = make([]GithubRepo, repo_count)

	rows, err := db.Query("SELECT r.id, username, project_name FROM streamer_repo r" +
	" JOIN streamer_userprofile_repos upr ON upr.repo_id = r.id " +
	" JOIN streamer_userprofile up ON up.id = upr.userprofile_id" +
	" WHERE up.user_id = ?;", user_id)
	// what's a good way to handle this not working?
	if (err != nil) {
		return nil, err
	}
	defer rows.Close()

	var username, project_name string
	var pk int
	for rows.Next() {
		err = rows.Scan(&pk, &username, &project_name)
		if (err != nil) {
			fmt.Println("Error!: ", err)
		}
		var repo = GithubRepo{pk, username, project_name}
		repos = append(repos, repo)
	}

	return repos, err
}

func get_user(db *sql.DB, uid int) (u User, err error) {
	row := db.QueryRow(
		"SELECT id, username, email" +
		" FROM auth_user" +
		" WHERE id = ?", uid)
	err = row.Scan(&u.Id, &u.username, &u.Email)
	return
}

func get_repo_activity(db *sql.DB, repo *GithubRepo) (activity_list []Activity, err error){
	activity_list = make([]Activity, 0)
	rows, err := db.Query(
		"SELECT a.id, a.event_id, a.type, a.created_at, ghu.name, r.username, r.project_name, meta" +
		" FROM streamer_activity a" +
		" JOIN streamer_repo r on r.id=a.repo_id" +
		" JOIN streamer_githubuser ghu on ghu.id=a.user_id" +
		" JOIN streamer_userprofile_repos upr on r.id=upr.repo_id" +
		" WHERE r.id = ?" +
		" AND a.created_at > DATE_SUB(NOW(), INTERVAL 5 day)" + // don't send things more than a few days old. Think, new users who subscribe to rails/rails
		" AND (upr.last_sent is null" + // hasn't been sent at all
		"   OR a.created_at > upr.last_sent)",  // or hasn't been sent since we've gotten new stuff
		repo.Id)
	if (err != nil) {
		return
	}
	defer rows.Close()

	var (
		pk, github_id, repo_id int
		activity_type, username, repo_project, repo_user, meta, created_str string
		created_at time.Time
	)

	for rows.Next() {
		err = rows.Scan(&pk, &github_id, &activity_type, &created_str, &username, &repo_user, &repo_project, &meta)
		if (err != nil) {
			fmt.Println("ERR: ", err)
		}

		created_at, err = time.Parse("2006-01-02 15:04:05", created_str)
		if (err != nil) {
			fmt.Println("Can't parse the format of ", created_str)
			return nil, err
		}

		activity_list = append(activity_list,
			Activity{pk, github_id, activity_type, created_at, username,
			GithubRepo{repo_id, repo_user, repo_project}, meta})
	}

	return
}

func commit_comment_render(activities []Activity, long_template bool) string { return "" }
func pull_request_comment_render(activities []Activity, long_template bool) string { return "" }
func member_render(activities []Activity, long_template bool) string { return "" }
func download_render(activities []Activity, long_template bool) string { return "" }
func fork_apply_render(activities []Activity, long_template bool) string { return "" }
func team_add_render(activities []Activity, long_template bool) string { return "" }
func gist_render(activities []Activity, long_template bool) string { return "" }
func follow_render(activities []Activity, long_template bool) string { return "" }

func repo_to_template(repo GithubRepo, activities []Activity, render_map map[string]func([]Activity, bool)string) string {
	if len(activities) == 0 {
		return ""
	}
	var activity_map = make(map[string][]Activity)
	for _, activity := range activities {
		// This seems like a lot of juggling. Is there a better way?
		arr := activity_map[activity.activity_type]
		if (arr == nil) {
			arr = make([]Activity, 0)
			activity_map[activity.activity_type] = arr
		}
		arr = append(arr, activity)
		activity_map[activity.activity_type] = arr
	}

	// activity_map: activity_type => []activity
	var response = fmt.Sprintf("%s:\n", repo.FullName())
	for activity_type, activities := range activity_map {
		fn, ok := render_map[activity_type]
		if ok {
			response += fn(activities, true)
		} else {
			log.Print("Not sure how to render activites of type ", activity_type)
		}
	}
	return response
}

func repo_to_string(db *sql.DB, repo GithubRepo, response chan string) {
	activities, err := get_repo_activity(db, &repo)
	if (err != nil) {
		fmt.Println("ERR: ", err)
		os.Exit(1)
	}

	activity_type_to_renderer := map[string]func([]Activity, bool)string {
		"P": push_render,
		"PR": pull_request_render,
		"D": delete_render,
		"C": create_render,
		"W": watch_render,
		"F": fork_render,
		"IC": issue_comment_render,
		"Gl": wiki_render, // Gl is for Gollum, Github's wiki thing.
		"I": issue_render,
		"Pb": public_render,
		// TODO: TA (team add), FA (fork apply),
		// TODO: DO (Download), RC (Pull Request Review Comment),
		// TODO: Fl (Follow), G (Gist)
	}

	response <- repo_to_template(repo, activities, activity_type_to_renderer)
}

func mark_user_repo_sent(db *sql.DB, user User, repos []GithubRepo) (err error) {
	// should find the streamer_userprofile_repo row for the repo
	// / user combo, mark its last_sent as now
	ids := make([]string, 0)
	for _, repo := range repos {
		// why are they 0?
		if repo.Id != 0 {
			ids = append(ids, strconv.FormatInt(int64(repo.Id), 10))
		}
	}

	// There is likely a better way to get parameterization, but
	// it wasn't working for me with ?'s.
	str := fmt.Sprintf(
		"UPDATE streamer_userprofile_repos " +
		"SET last_sent=NOW() " +
		"WHERE repo_id IN (%s)", strings.Join(ids, ","))
	_, err = db.Exec(str)
	return
}

var user_id = flag.Int("user_id", 1, "ID of user to output.")
var email_to = flag.String("email_to", "", "Email address of who should get the report.")
var mark_read = flag.Bool("mark_read", true, "Whether to mark activity sent as read. False means subsequent calls will send the same info.")

// TODO: Github Users

func main() {
	flag.Parse()

	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@/%s?charset=utf8", os.Getenv("DB_USER"), os.Getenv("DB_PASS"), os.Getenv("DB_DB")))
	if (err != nil) {
		log.Fatalf("Unable to connect to mysql. ", err)
	}
	defer db.Close()

	repos, err := get_users_repos(db, *user_id)
	if err != nil {
		log.Fatalf("Error fetching user's repos. %s", err)
	}

	response_chan := make(chan string, len(repos))
	for _, repo := range repos {
		repo_to_string(db, repo, response_chan)
	}

	var response string
	for num_responses := 0; num_responses < len(repos); {
		select {
		case r := <-response_chan:
			num_responses++
			response += r
		}
	}

	// should fetch this user based on uid.
	user, err := get_user(db, *user_id)
	if err != nil { log.Fatalf("Couldn't return user %d. %s", *user_id, err) }
	if *mark_read {
		fmt.Println("Marking read.")
		err = mark_user_repo_sent(db, user, repos)
		if err != nil {
			log.Fatalf("Error updating repositories as sent.")
		}
	}

	if len(response) == 0 {
		log.Fatal("No content to email.")
	}

	if len(*email_to) > 0 {
		mg := mailgun.Open(os.Getenv("MAILGUN_API_KEY"))
		e := &Email{
			from: "justin@gitstreams.mailgun.org",
			to: []string{user.Email},
			subject: "Gitstreams Digest Email",
			text: response,
		}

		id, err := mg.Send(e)

		if err != nil {
			log.Fatal("Unable to send email. ", err)
		}
		log.Printf("MessageId = %s for uid:%s", id, user.Id)
	} else {
		fmt.Println("Would have sent the following email.")
		fmt.Println(response)
	}
	return
}
<commit_msg>cleaning up notes / organization.<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
	_ "github.com/go-sql-driver/mysql"
	mailgun "github.com/riobard/go-mailgun"
)

type User struct {
	Id int
	username string
	Email string
}

type GithubUser struct {
	Id int
	Login string
	Url string
}

// db thing.
type GithubRepo struct {
	Id int
	User string // should probably be Login
	RepoName string // should probably be Name
}

func (g *GithubRepo) FullName() string {
	return fmt.Sprintf("%s/%s", g.User, g.RepoName)
}

type GithubApiRepo struct {
	Id int
	Owner GithubUser
	Name NString
	Description NString
}

func (g *GithubApiRepo) FullName() string {
	return fmt.Sprintf("%s/%s", g.Owner.Login, g.Name)
}

type Activity struct {
	Id int
	github_id int
	activity_type string // should be enum
	created_at time.Time
	Username string
	repo GithubRepo // this isn't going to be memory efficient
	Meta string // full payload of json object
}

type Treeish struct {
	Label string
	Sha string
	Repo GithubApiRepo
	Html_url string
	User GithubUser
}

type Commit struct {
	Sha string
	Message string
	Author struct {
		Name string
		Email string
	}
}

func (c *Commit) ShortSha() string {
	return c.Sha[0:6]
}

func (c *Commit) ShortCommit() string {
	msg := strings.Split(c.Message, "\n")[0]
	if len(msg) > 80 {
		return msg[0:77] + "..."
	}
	return msg
}

// Nullable String
type NString string

func (n *NString) UnmarshalJSON(b []byte) (err error) {
	if string(b) == "null" {
		return nil
	}
	return json.Unmarshal(b, (*string)(n))
}

func get_users_repos(db *sql.DB, user_id int) ([]GithubRepo, error) {
	var repo_count int

	// TODO(justinabrahms): Should really alter the schema such
	// that the join from user <-> repo doesn't go through
	// userprofiles.
	row := db.QueryRow("SELECT count(*) FROM streamer_repo r" +
	" JOIN streamer_userprofile_repos upr ON upr.repo_id = r.id " +
	" JOIN streamer_userprofile up ON up.id = upr.userprofile_id" +
	" WHERE up.user_id = ?;", user_id)

	err := row.Scan(&repo_count)
	if (err != nil) {
		return nil, err
	}
	var repos = make([]GithubRepo, repo_count)

	rows, err := db.Query("SELECT r.id, username, project_name FROM streamer_repo r" +
	" JOIN streamer_userprofile_repos upr ON upr.repo_id = r.id " +
	" JOIN streamer_userprofile up ON up.id = upr.userprofile_id" +
	" WHERE up.user_id = ?;", user_id)
	// what's a good way to handle this not working?
	if (err != nil) {
		return nil, err
	}
	defer rows.Close()

	var username, project_name string
	var pk int
	for rows.Next() {
		err = rows.Scan(&pk, &username, &project_name)
		if (err != nil) {
			fmt.Println("Error!: ", err)
		}
		var repo = GithubRepo{pk, username, project_name}
		repos = append(repos, repo)
	}

	return repos, err
}

func get_user(db *sql.DB, uid int) (u User, err error) {
	row := db.QueryRow(
		"SELECT id, username, email" +
		" FROM auth_user" +
		" WHERE id = ?", uid)
	err = row.Scan(&u.Id, &u.username, &u.Email)
	return
}

func get_repo_activity(db *sql.DB, repo *GithubRepo) (activity_list []Activity, err error){
	activity_list = make([]Activity, 0)
	rows, err := db.Query(
		"SELECT a.id, a.event_id, a.type, a.created_at, ghu.name, r.username, r.project_name, meta" +
		" FROM streamer_activity a" +
		" JOIN streamer_repo r on r.id=a.repo_id" +
		" JOIN streamer_githubuser ghu on ghu.id=a.user_id" +
		" JOIN streamer_userprofile_repos upr on r.id=upr.repo_id" +
		" WHERE r.id = ?" +
		" AND a.created_at > DATE_SUB(NOW(), INTERVAL 5 day)" + // don't send things more than a few days old. Think, new users who subscribe to rails/rails
		" AND (upr.last_sent is null" + // hasn't been sent at all
		"   OR a.created_at > upr.last_sent)",  // or hasn't been sent since we've gotten new stuff
		repo.Id)
	if (err != nil) {
		return
	}
	defer rows.Close()

	var (
		pk, github_id, repo_id int
		activity_type, username, repo_project, repo_user, meta, created_str string
		created_at time.Time
	)

	for rows.Next() {
		err = rows.Scan(&pk, &github_id, &activity_type, &created_str, &username, &repo_user, &repo_project, &meta)
		if (err != nil) {
			fmt.Println("ERR: ", err)
		}

		created_at, err = time.Parse("2006-01-02 15:04:05", created_str)
		if (err != nil) {
			fmt.Println("Can't parse the format of ", created_str)
			return nil, err
		}

		activity_list = append(activity_list,
			Activity{pk, github_id, activity_type, created_at, username,
			GithubRepo{repo_id, repo_user, repo_project}, meta})
	}

	return
}

func commit_comment_render(activities []Activity, long_template bool) string { return "" }
func pull_request_comment_render(activities []Activity, long_template bool) string { return "" }
func member_render(activities []Activity, long_template bool) string { return "" }
func download_render(activities []Activity, long_template bool) string { return "" }
func fork_apply_render(activities []Activity, long_template bool) string { return "" }
func team_add_render(activities []Activity, long_template bool) string { return "" }
func gist_render(activities []Activity, long_template bool) string { return "" }
func follow_render(activities []Activity, long_template bool) string { return "" }

func repo_to_template(repo GithubRepo, activities []Activity, render_map map[string]func([]Activity, bool)string) string {
	if len(activities) == 0 {
		return ""
	}
	var activity_map = make(map[string][]Activity)
	for _, activity := range activities {
		// This seems like a lot of juggling. Is there a better way?
		arr := activity_map[activity.activity_type]
		if (arr == nil) {
			arr = make([]Activity, 0)
			activity_map[activity.activity_type] = arr
		}
		arr = append(arr, activity)
		activity_map[activity.activity_type] = arr
	}

	// activity_map: activity_type => []activity
	var response = fmt.Sprintf("%s:\n", repo.FullName())
	for activity_type, activities := range activity_map {
		fn, ok := render_map[activity_type]
		if ok {
			response += fn(activities, true)
		} else {
			log.Print("Not sure how to render activites of type ", activity_type)
		}
	}
	return response
}

func repo_to_string(db *sql.DB, repo GithubRepo, response chan string) {
	activities, err := get_repo_activity(db, &repo)
	if (err != nil) {
		fmt.Println("ERR: ", err)
		os.Exit(1)
	}

	activity_type_to_renderer := map[string]func([]Activity, bool)string {
		"P": push_render,
		"PR": pull_request_render,
		"D": delete_render,
		"C": create_render,
		"W": watch_render,
		"F": fork_render,
		"IC": issue_comment_render,
		"Gl": wiki_render, // Gl is for Gollum, Github's wiki thing.
		"I": issue_render,
		"Pb": public_render,
	}

	response <- repo_to_template(repo, activities, activity_type_to_renderer)
}

func mark_user_repo_sent(db *sql.DB, user User, repos []GithubRepo) (err error) {
	// finds the streamer_userprofile_repo row for the repo / user
	// combo, mark its last_sent as now
	ids := make([]string, 0)
	for _, repo := range repos {
		// TODO: why are they 0?
		if repo.Id != 0 {
			ids = append(ids, strconv.FormatInt(int64(repo.Id), 10))
		}
	}

	// There is likely a better way to get parameterization, but
	// it wasn't working for me with ?'s.
	str := fmt.Sprintf(
		"UPDATE streamer_userprofile_repos " +
		"SET last_sent=NOW() " +
		"WHERE repo_id IN (%s)", strings.Join(ids, ","))
	_, err = db.Exec(str)
	return
}

// TODO: Github Users

// TODO: Need to finish the following activity types:
// - TA (team add)
// - FA (fork apply)
// - DO (Download)
// - RC (Pull Request Review Comment)
// - Fl (Follow)
// - G (Gist)
// - M (Member)
// - CC (Commit Comment)

var user_id = flag.Int("user_id", 1, "ID of user to output.")
var send_email = flag.Bool("send_email", false, "Whether to send the email to the user.")
var mark_read = flag.Bool("mark_read", true, "Whether to mark activity sent as read. False means subsequent calls will send the same info.")

func main() {
	// Expects the following environment variables:
	// DB_USER, DB_PASS, DB_DB  (database user, password and database)
	// MAILGUN_API_KEY  (api key from mailgun.org)

	flag.Parse()

	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@/%s?charset=utf8", os.Getenv("DB_USER"), os.Getenv("DB_PASS"), os.Getenv("DB_DB")))
	if (err != nil) {
		log.Fatalf("Unable to connect to mysql. ", err)
	}
	defer db.Close()

	repos, err := get_users_repos(db, *user_id)
	if err != nil {
		log.Fatalf("Error fetching user's repos. %s", err)
	}

	response_chan := make(chan string, len(repos))
	for _, repo := range repos {
		repo_to_string(db, repo, response_chan)
	}

	var response string
	for num_responses := 0; num_responses < len(repos); {
		select {
		case r := <-response_chan:
			num_responses++
			response += r
		}
	}

	// should fetch this user based on uid.
	user, err := get_user(db, *user_id)
	if err != nil { log.Fatalf("Couldn't return user %d. %s", *user_id, err) }
	if *mark_read {
		fmt.Println("Marking read.")
		err = mark_user_repo_sent(db, user, repos)
		if err != nil {
			log.Fatalf("Error updating repositories as sent.")
		}
	}

	if len(response) == 0 {
		log.Fatal("No content to email.")
	}

	if len(*send_email) > 0 {
		mg := mailgun.Open(os.Getenv("MAILGUN_API_KEY"))
		e := &Email{
			from: "justin@gitstreams.mailgun.org",
			to: []string{user.Email},
			subject: "Gitstreams Digest Email",
			text: response,
		}

		id, err := mg.Send(e)

		if err != nil {
			log.Fatal("Unable to send email. ", err)
		}
		log.Printf("MessageId = %s for uid:%s", id, user.Id)
	} else {
		fmt.Println("Would have sent the following email.")
		fmt.Println(response)
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/koron/gomigemo/embedict"
	"github.com/koron/gomigemo/migemo"
)

const version = "0.1.0"

var flag_n = flag.Bool("n", false, "print line number with output lines")

type grepOpt struct {
	optNumber   bool
	optFilename bool
	filename    string
}

func main() {
	st := _main()
	os.Exit(st)
}

func _main() int {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "migemogrep v%s\n\nUsage: migemogrep [options] pattern [files...]\n", version)
		flag.PrintDefaults()
	}
	var dictPath = flag.String("d", "", "Alternate location to dictionary")

	flag.Parse()

	if flag.NArg() == 0 {
		flag.Usage()
		return 1
	}

	var dict migemo.Dict
	var err error
	if *dictPath == "" {
		dict, err = embedict.Load()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
	} else {
		dict, err = migemo.Load(*dictPath)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
	}

	re, err := migemo.Compile(dict, flag.Arg(0))
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}

	opt := &grepOpt{
		optNumber:   *flag_n,
		optFilename: flag.NArg() > 2,
	}

	// If there's only one arg, then we need to match against the input
	if flag.NArg() == 1 {
		opt.filename = "stdin"

		if err = grep(os.Stdin, re, opt); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}

		// We got here, we're fine.
		return 0
	}

	// More than one arg. We must be searching against a file
	for _, arg := range flag.Args()[1:] {
		f, err := os.Open(arg)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
		defer f.Close()

		opt.filename = arg
		if err = grep(f, re, opt); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
	}

	return 0
}
<commit_msg>add -H to show filename<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/koron/gomigemo/embedict"
	"github.com/koron/gomigemo/migemo"
)

const version = "0.1.0"

var flag_n = flag.Bool("n", false, "print line number with output lines")
var flag_H = flag.Bool("H", false, "print the filename for each match")

type grepOpt struct {
	optNumber   bool
	optFilename bool
	filename    string
}

func main() {
	st := _main()
	os.Exit(st)
}

func _main() int {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "migemogrep v%s\n\nUsage: migemogrep [options] pattern [files...]\n", version)
		flag.PrintDefaults()
	}
	var dictPath = flag.String("d", "", "Alternate location to dictionary")

	flag.Parse()

	if flag.NArg() == 0 {
		flag.Usage()
		return 1
	}

	var dict migemo.Dict
	var err error
	if *dictPath == "" {
		dict, err = embedict.Load()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
	} else {
		dict, err = migemo.Load(*dictPath)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
	}

	re, err := migemo.Compile(dict, flag.Arg(0))
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}

	opt := &grepOpt{
		optNumber:   *flag_n,
		optFilename: *flag_H || flag.NArg() > 2,
	}

	// If there's only one arg, then we need to match against the input
	if flag.NArg() == 1 {
		opt.filename = "stdin"

		if err = grep(os.Stdin, re, opt); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}

		// We got here, we're fine.
		return 0
	}

	// More than one arg. We must be searching against a file
	for _, arg := range flag.Args()[1:] {
		f, err := os.Open(arg)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
		defer f.Close()

		opt.filename = arg
		if err = grep(f, re, opt); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
	}

	return 0
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"io/ioutil"

	"github.com/gorilla/handlers"
)

const (
	// Name of the application
	Name = "simple webserver"
	// Version of the application
	Version = "1.1.0"
)

func main() {
	var (
		listenFlag = flag.String("listen", EnvOrDefault("SIMPLE_WEBSERVER_LISTEN", ":8082"), "Address + Port to listen on. Format ip:port. Environment variable: SIMPLE_WEBSERVER_LISTEN")
		redisFlag  = flag.String("redis", EnvOrDefault("SIMPLE_WEBSERVER_REDIS", ":6379"), "Address + Port where a redis server is listening. Environment variable: SIMPLE_WEBSERVER_REDIS")
	)
	flag.Parse()

	// Create Redis storage
	r := NewRedisStorage(*redisFlag)

	// Define HTTP endpoints
	s := http.NewServeMux()
	s.HandleFunc("/", RootHandler)
	s.HandleFunc("/ping", PingHandler(r))
	s.HandleFunc("/kill", KillHandler)
	s.HandleFunc("/version", VersionHandler)
	s.HandleFunc("/payload", PayloadHandler)

	// Bootstrap logger
	logger := log.New(os.Stdout, "", log.LstdFlags)
	logger.Printf("Starting webserver and listen on %s", *listenFlag)

	// Start HTTP Server with request logging
	loggingHandler := handlers.LoggingHandler(os.Stdout, s)
	log.Fatal(http.ListenAndServe(*listenFlag, loggingHandler))
}

// RootHandler handles requests to the "/" path.
// It will redirect the request to /ping with a 303 HTTP header
func RootHandler(resp http.ResponseWriter, req *http.Request) {
	http.Redirect(resp, req, "/ping", http.StatusSeeOther)
}

// PingHandler handles request to the "/ping" endpoint.
// It will send a PING request to Redis and return the response
// of the NoSQL database.
// The response is obvious: "pong" :)
func PingHandler(s Storage) http.HandlerFunc {
	return func(resp http.ResponseWriter, req *http.Request) {
		res, err := s.Ping()
		if err != nil {
			resp.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(resp, err.Error())
			return
		}
		resp.WriteHeader(http.StatusOK)
		fmt.Fprintln(resp, res)
	}
}

// KillHandler handles request to the "/kill" endpoint.
// Will shut down the webserver immediately (via exit code 1).
// Only DELETE requests are accepted.
// Other request methods will throw a HTTP Status Code 405 (Method Not Allowed)
func KillHandler(resp http.ResponseWriter, req *http.Request) {
	if req.Method != "DELETE" {
		resp.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	// We need to send a HTTP Status Code 200 (OK)
	// to respond that we have accepted the request.
	// Here we send a chunked response to the requester.
	flusher, ok := resp.(http.Flusher)
	if !ok {
		resp.WriteHeader(http.StatusInternalServerError)
		return
	}
	resp.WriteHeader(http.StatusOK)
	flusher.Flush()

	// And we kill the server (like requested)
	os.Exit(1)
}

// VersionHandler handles request to the "/version" endpoint.
// It prints the Name and Version of this app.
func VersionHandler(resp http.ResponseWriter, req *http.Request) {
	resp.WriteHeader(http.StatusOK)
	fmt.Fprintf(resp, "%s v%s\n", Name, Version)
}

// PayloadHandler handles request to the "/payload" endpoint.
// It is a debug route to dump the complete request incl. method, header and body.
func PayloadHandler(resp http.ResponseWriter, req *http.Request) {
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		resp.WriteHeader(http.StatusInternalServerError)
		return
	}

	resp.WriteHeader(http.StatusOK)
	log.Printf("Method: %s\n", req.Method)
	fmt.Fprintf(resp, "Method: %s\n", req.Method)

	if len(req.Header) > 0 {
		log.Println("Headers:")
		fmt.Fprint(resp, "Headers:\n")
		for key, values := range req.Header {
			for _, val := range values {
				log.Printf("%s: %s\n", key, val)
				fmt.Fprintf(resp, "%s: %s\n", key, val)
			}
		}
	}

	log.Printf("Payload: %s", string(body))
	fmt.Fprintf(resp, "Payload: %s", string(body))
}

// EnvOrDefault will read env from the environment.
// If the environment variable is not set in the environment
// fallback will be returned.
// This function can be used as a value for flag.String to enable
// env var support for your binary flags.
func EnvOrDefault(env, fallback string) string {
	value := fallback
	if v := os.Getenv(env); v != "" {
		value = v
	}

	return value
}
<commit_msg>Set version to v1.1.1<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"io/ioutil"

	"github.com/gorilla/handlers"
)

const (
	// Name of the application
	Name = "simple webserver"
	// Version of the application
	Version = "1.1.1"
)

func main() {
	var (
		listenFlag = flag.String("listen", EnvOrDefault("SIMPLE_WEBSERVER_LISTEN", ":8082"), "Address + Port to listen on. Format ip:port. Environment variable: SIMPLE_WEBSERVER_LISTEN")
		redisFlag  = flag.String("redis", EnvOrDefault("SIMPLE_WEBSERVER_REDIS", ":6379"), "Address + Port where a redis server is listening. Environment variable: SIMPLE_WEBSERVER_REDIS")
	)
	flag.Parse()

	// Create Redis storage
	r := NewRedisStorage(*redisFlag)

	// Define HTTP endpoints
	s := http.NewServeMux()
	s.HandleFunc("/", RootHandler)
	s.HandleFunc("/ping", PingHandler(r))
	s.HandleFunc("/kill", KillHandler)
	s.HandleFunc("/version", VersionHandler)
	s.HandleFunc("/payload", PayloadHandler)

	// Bootstrap logger
	logger := log.New(os.Stdout, "", log.LstdFlags)
	logger.Printf("Starting webserver and listen on %s", *listenFlag)

	// Start HTTP Server with request logging
	loggingHandler := handlers.LoggingHandler(os.Stdout, s)
	log.Fatal(http.ListenAndServe(*listenFlag, loggingHandler))
}

// RootHandler handles requests to the "/" path.
// It will redirect the request to /ping with a 303 HTTP header
func RootHandler(resp http.ResponseWriter, req *http.Request) {
	http.Redirect(resp, req, "/ping", http.StatusSeeOther)
}

// PingHandler handles request to the "/ping" endpoint.
// It will send a PING request to Redis and return the response
// of the NoSQL database.
// The response is obvious: "pong" :)
func PingHandler(s Storage) http.HandlerFunc {
	return func(resp http.ResponseWriter, req *http.Request) {
		res, err := s.Ping()
		if err != nil {
			resp.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(resp, err.Error())
			return
		}
		resp.WriteHeader(http.StatusOK)
		fmt.Fprintln(resp, res)
	}
}

// KillHandler handles request to the "/kill" endpoint.
// Will shut down the webserver immediately (via exit code 1).
// Only DELETE requests are accepted.
// Other request methods will throw a HTTP Status Code 405 (Method Not Allowed)
func KillHandler(resp http.ResponseWriter, req *http.Request) {
	if req.Method != "DELETE" {
		resp.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	// We need to send a HTTP Status Code 200 (OK)
	// to respond that we have accepted the request.
	// Here we send a chunked response to the requester.
	flusher, ok := resp.(http.Flusher)
	if !ok {
		resp.WriteHeader(http.StatusInternalServerError)
		return
	}
	resp.WriteHeader(http.StatusOK)
	flusher.Flush()

	// And we kill the server (like requested)
	os.Exit(1)
}

// VersionHandler handles request to the "/version" endpoint.
// It prints the Name and Version of this app.
func VersionHandler(resp http.ResponseWriter, req *http.Request) {
	resp.WriteHeader(http.StatusOK)
	fmt.Fprintf(resp, "%s v%s\n", Name, Version)
}

// PayloadHandler handles request to the "/payload" endpoint.
// It is a debug route to dump the complete request incl. method, header and body.
func PayloadHandler(resp http.ResponseWriter, req *http.Request) {
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		resp.WriteHeader(http.StatusInternalServerError)
		return
	}

	resp.WriteHeader(http.StatusOK)
	log.Printf("Method: %s\n", req.Method)
	fmt.Fprintf(resp, "Method: %s\n", req.Method)

	if len(req.Header) > 0 {
		log.Println("Headers:")
		fmt.Fprint(resp, "Headers:\n")
		for key, values := range req.Header {
			for _, val := range values {
				log.Printf("%s: %s\n", key, val)
				fmt.Fprintf(resp, "%s: %s\n", key, val)
			}
		}
	}

	log.Printf("Payload: %s", string(body))
	fmt.Fprintf(resp, "Payload: %s", string(body))
}

// EnvOrDefault will read env from the environment.
// If the environment variable is not set in the environment
// fallback will be returned.
// This function can be used as a value for flag.String to enable
// env var support for your binary flags.
func EnvOrDefault(env, fallback string) string {
	value := fallback
	if v := os.Getenv(env); v != "" {
		value = v
	}

	return value
}
<|endoftext|>


<commit_before>package main

import (
	"firempq/common"
	"firempq/defs"
	"firempq/facade"
	"firempq/features/pqueue"
	"firempq/server"
	"github.com/op/go-logging"
	"os"
	//	"runtime"
	//	"runtime/pprof"
	"strconv"
	//	"time"
)

var log = logging.MustGetLogger("firempq")

func init_logging() {
	format := logging.MustStringFormatter(
		"%{color}%{time:2006-01-02 15:04:05.00000}: %{level}%{color:reset} %{shortfile} %{message}",
	)
	logbackend := logging.NewLogBackend(os.Stderr, "", 0)
	formatter := logging.NewBackendFormatter(logbackend, format)
	logging.SetBackend(formatter)
	logging.SetLevel(logging.DEBUG, "firempq")
}

func main() {
	init_logging()
	srv, err := server.GetServer(server.SIMPLE_SERVER, ":9033")
	if err != nil {
		log.Critical("Error: %s", err.Error())
	}

	srv.Start()
	//time.Sleep(1E9)
	//srv.Stop()
}

func addMessages(pq common.ISvc) {
	//	ts := time.Now().UnixNano()
	payload := "0000"
	//payload += payload
	//	payload += payload
	//	payload += payload
	//	payload += payload
	//	payload += payload
	//time.Sleep(60 * 1000000000)
	//pq.DeleteAll()
	v := []string{defs.PRM_PRIORITY, "1", defs.PRM_PAYLOAD, payload}
	for i := 0; i < 10000000; i++ {
		pq.Call(pqueue.ACTION_PUSH, v)
	}
	//end_t := time.Now().UnixNano()

	//fmt.Println((end_t - ts) / 1000000)
}

func main1() {
	init_logging()
	//	f, _ := os.Create("pp.dat")
	//	pprof.StartCPUProfile(f)
	//	defer pprof.StopCPUProfile()
	//
	//	runtime.GOMAXPROCS(runtime.NumCPU())

	fc := facade.CreateFacade()
	defer fc.Close()
	for i := 0; i < 1; i++ {
		qid := "tst_queue_" + strconv.Itoa(i)
		err := fc.CreateService(common.STYPE_PRIORITY_QUEUE, qid, nil)
		if err != nil {
			log.Notice("%s: %s", err, qid)
		}
	}
	start_ts := common.Uts()
	log.Notice("Started")
	for i := 0; i < 1; i++ {
		qid := "tst_queue_" + strconv.Itoa(i)
		q, _ := fc.GetService(qid)
		addMessages(q)
	}
	log.Notice("Finished. Elapsed: %d", common.Uts()-start_ts)
}
<commit_msg>Removed bunch of commented lines.<commit_after>package main

import (
	"firempq/common"
	"firempq/defs"
	"firempq/facade"
	"firempq/features/pqueue"
	"firempq/server"
	"os"
	"strconv"

	"github.com/op/go-logging"
)

var log = logging.MustGetLogger("firempq")

func init_logging() {
	format := logging.MustStringFormatter(
		"%{color}%{time:2006-01-02 15:04:05.00000}: %{level}%{color:reset} %{shortfile} %{message}",
	)
	logbackend := logging.NewLogBackend(os.Stderr, "", 0)
	formatter := logging.NewBackendFormatter(logbackend, format)
	logging.SetBackend(formatter)
	logging.SetLevel(logging.DEBUG, "firempq")
}

func main() {
	init_logging()
	srv, err := server.GetServer(server.SIMPLE_SERVER, ":9033")
	if err != nil {
		log.Critical("Error: %s", err.Error())
	}

	srv.Start()
	//time.Sleep(1E9)
	//srv.Stop()
}

func addMessages(pq common.ISvc) {
	payload := "0000"
	v := []string{defs.PRM_PRIORITY, "1", defs.PRM_PAYLOAD, payload}
	for i := 0; i < 10000000; i++ {
		pq.Call(pqueue.ACTION_PUSH, v)
	}
}

func main1() {
	init_logging()
	//	f, _ := os.Create("pp.dat")
	//	pprof.StartCPUProfile(f)
	//	defer pprof.StopCPUProfile()
	//
	//	runtime.GOMAXPROCS(runtime.NumCPU())

	fc := facade.CreateFacade()
	defer fc.Close()
	for i := 0; i < 1; i++ {
		qid := "tst_queue_" + strconv.Itoa(i)
		err := fc.CreateService(common.STYPE_PRIORITY_QUEUE, qid, nil)
		if err != nil {
			log.Notice("%s: %s", err, qid)
		}
	}
	start_ts := common.Uts()
	log.Notice("Started")
	for i := 0; i < 1; i++ {
		qid := "tst_queue_" + strconv.Itoa(i)
		q, _ := fc.GetService(qid)
		addMessages(q)
	}
	log.Notice("Finished. Elapsed: %d", common.Uts()-start_ts)
}
<|endoftext|>


<commit_before>// Copyright 2015 realglobe, Inc.
//
// 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 main

import (
	idpapi "github.com/realglobe-Inc/edo-idp-selector/api/idp"
	idpdb "github.com/realglobe-Inc/edo-idp-selector/database/idp"
	"github.com/realglobe-Inc/edo-idp-selector/database/session"
	tadb "github.com/realglobe-Inc/edo-idp-selector/database/ta"
	webdb "github.com/realglobe-Inc/edo-idp-selector/database/web"
	idperr "github.com/realglobe-Inc/edo-idp-selector/error"
	"github.com/realglobe-Inc/edo-idp-selector/page/idpselect"
	"github.com/realglobe-Inc/edo-lib/driver"
	logutil "github.com/realglobe-Inc/edo-lib/log"
	"github.com/realglobe-Inc/edo-lib/rand"
	"github.com/realglobe-Inc/edo-lib/server"
	"github.com/realglobe-Inc/go-lib/erro"
	"github.com/realglobe-Inc/go-lib/rglog"
	"html/template"
	"net/http"
	"os"
	"strings"
	"time"
)

func main() {
	var exitCode = 0
	defer func() {
		if exitCode != 0 {
			os.Exit(exitCode)
		}
	}()
	defer rglog.Flush()

	logutil.InitConsole("github.com/realglobe-Inc")

	param, err := parseParameters(os.Args...)
	if err != nil {
		log.Err(erro.Unwrap(err))
		log.Debug(erro.Wrap(err))
		exitCode = 1
		return
	}

	logutil.SetupConsole("github.com/realglobe-Inc", param.consLv)
	if err := logutil.Setup("github.com/realglobe-Inc", param.logType, param.logLv, param); err != nil {
		log.Err(erro.Unwrap(err))
		log.Debug(erro.Wrap(err))
		exitCode = 1
		return
	}

	if err := serve(param); err != nil {
		log.Err(erro.Unwrap(err))
		log.Debug(erro.Wrap(err))
		exitCode = 1
		return
	}

	log.Info("Shut down")
}

func serve(param *parameters) (err error) {
	// バックエンドの準備。

	redPools := driver.NewRedisPoolSet(param.redTimeout, param.redPoolSize, param.redPoolExpIn)
	defer redPools.Close()
	monPools := driver.NewMongoPoolSet(param.monTimeout)
	defer monPools.Close()

	// web データ。
	var webDb webdb.Db
	switch param.webDbType {
	case "direct":
		webDb = webdb.NewDirectDb()
		log.Info("Get web data directly")
	case "redis":
		webDb = webdb.NewRedisCache(webdb.NewDirectDb(), redPools.Get(param.webDbAddr), param.webDbTag, param.webDbExpIn)
		log.Info("Get web data with redis " + param.webDbAddr + "<" + param.webDbTag + ">")
	default:
		return erro.New("invalid web data DB type " + param.webDbType)
	}

	// IdP 情報。
	var idpDb idpdb.Db
	switch param.idpDbType {
	case "mongo":
		pool, err := monPools.Get(param.idpDbAddr)
		if err != nil {
			return erro.Wrap(err)
		}
		idpDb = idpdb.NewMongoDb(pool, param.idpDbTag, param.idpDbTag2, webDb)
		log.Info("Use IdP info in mongodb " + param.idpDbAddr + "<" + param.idpDbTag + "." + param.idpDbTag2 + ">")
	default:
		return erro.New("invalid IdP DB type " + param.idpDbType)
	}

	// TA 情報。
	var taDb tadb.Db
	switch param.taDbType {
	case "mongo":
		pool, err := monPools.Get(param.taDbAddr)
		if err != nil {
			return erro.Wrap(err)
		}
		taDb = tadb.NewMongoDb(pool, param.taDbTag, param.taDbTag2, webDb)
		log.Info("Use TA info in mongodb " + param.taDbAddr + "<" + param.taDbTag + "." + param.taDbTag2 + ">")
	default:
		return erro.New("invalid TA DB type " + param.taDbType)
	}

	// セッション。
	var sessDb session.Db
	switch param.sessDbType {
	case "memory":
		sessDb = session.NewMemoryDb()
		log.Info("Save sessions in memory")
	case "redis":
		sessDb = session.NewRedisDb(redPools.Get(param.sessDbAddr), param.sessDbTag)
		log.Info("Save sessions in redis " + param.sessDbAddr + "<" + param.sessDbTag + ">")
	default:
		return erro.New("invalid session DB type " + param.sessDbType)
	}

	var errTmpl *template.Template
	if param.tmplErr != "" {
		errTmpl, err = template.ParseFiles(param.tmplErr)
		if err != nil {
			return erro.Wrap(err)
		}
	}

	idGen := rand.New(time.Minute)

	// バックエンドの準備完了。

	stopper := server.NewStopper()
	defer func() {
		stopper.Lock()
		defer stopper.Unlock()
		for stopper.Stopped() {
			stopper.Wait()
		}
	}()

	if param.debug {
		idperr.Debug = true
	}

	selPage := idpselect.New(
		stopper,
		param.pathSelUi,
		errTmpl,
		param.sessLabel,
		param.sessLen,
		param.sessExpIn,
		param.sessRefDelay,
		param.sessDbExpIn,
		param.ticLen,
		param.ticExpIn,
		idpDb,
		taDb,
		sessDb,
		idGen,
		param.cookPath,
		param.cookSec,
		param.debug,
	)

	mux := http.NewServeMux()
	routes := map[string]bool{}
	mux.HandleFunc(param.pathOk, idperr.WrapPage(stopper, func(w http.ResponseWriter, r *http.Request) error {
		return nil
	}, errTmpl))
	routes[param.pathOk] = true
	mux.HandleFunc(param.pathStart, selPage.HandleStart)
	routes[param.pathStart] = true
	mux.HandleFunc(param.pathSel, selPage.HandleSelect)
	routes[param.pathSel] = true
	mux.Handle(param.pathIdp, idpapi.New(stopper, idpDb, param.debug))
	routes[param.pathIdp] = true
	if param.uiDir != "" {
		// ファイル配信も自前でやる。
		pathUi := strings.TrimRight(param.pathUi, "/") + "/"
		mux.Handle(pathUi, http.StripPrefix(pathUi, http.FileServer(http.Dir(param.uiDir))))
		routes[param.pathUi] = true
	}

	if !routes["/"] {
		mux.HandleFunc("/", idperr.WrapPage(stopper, func(w http.ResponseWriter, r *http.Request) error {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "invalid endpoint", http.StatusNotFound, nil))
		}, errTmpl))
	}

	return server.Serve(param, mux)
}
<commit_msg>処理位置整理<commit_after>// Copyright 2015 realglobe, Inc.
//
// 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 main

import (
	idpapi "github.com/realglobe-Inc/edo-idp-selector/api/idp"
	idpdb "github.com/realglobe-Inc/edo-idp-selector/database/idp"
	"github.com/realglobe-Inc/edo-idp-selector/database/session"
	tadb "github.com/realglobe-Inc/edo-idp-selector/database/ta"
	webdb "github.com/realglobe-Inc/edo-idp-selector/database/web"
	idperr "github.com/realglobe-Inc/edo-idp-selector/error"
	"github.com/realglobe-Inc/edo-idp-selector/page/idpselect"
	"github.com/realglobe-Inc/edo-lib/driver"
	logutil "github.com/realglobe-Inc/edo-lib/log"
	"github.com/realglobe-Inc/edo-lib/rand"
	"github.com/realglobe-Inc/edo-lib/server"
	"github.com/realglobe-Inc/go-lib/erro"
	"github.com/realglobe-Inc/go-lib/rglog"
	"html/template"
	"net/http"
	"os"
	"strings"
	"time"
)

func main() {
	var exitCode = 0
	defer func() {
		if exitCode != 0 {
			os.Exit(exitCode)
		}
	}()
	defer rglog.Flush()

	logutil.InitConsole("github.com/realglobe-Inc")

	param, err := parseParameters(os.Args...)
	if err != nil {
		log.Err(erro.Unwrap(err))
		log.Debug(erro.Wrap(err))
		exitCode = 1
		return
	}

	logutil.SetupConsole("github.com/realglobe-Inc", param.consLv)
	if err := logutil.Setup("github.com/realglobe-Inc", param.logType, param.logLv, param); err != nil {
		log.Err(erro.Unwrap(err))
		log.Debug(erro.Wrap(err))
		exitCode = 1
		return
	}

	if err := serve(param); err != nil {
		log.Err(erro.Unwrap(err))
		log.Debug(erro.Wrap(err))
		exitCode = 1
		return
	}

	log.Info("Shut down")
}

func serve(param *parameters) (err error) {
	// バックエンドの準備。

	stopper := server.NewStopper()

	redPools := driver.NewRedisPoolSet(param.redTimeout, param.redPoolSize, param.redPoolExpIn)
	defer redPools.Close()
	monPools := driver.NewMongoPoolSet(param.monTimeout)
	defer monPools.Close()

	// web データ。
	var webDb webdb.Db
	switch param.webDbType {
	case "direct":
		webDb = webdb.NewDirectDb()
		log.Info("Get web data directly")
	case "redis":
		webDb = webdb.NewRedisCache(webdb.NewDirectDb(), redPools.Get(param.webDbAddr), param.webDbTag, param.webDbExpIn)
		log.Info("Get web data with redis " + param.webDbAddr + "<" + param.webDbTag + ">")
	default:
		return erro.New("invalid web data DB type " + param.webDbType)
	}

	// IdP 情報。
	var idpDb idpdb.Db
	switch param.idpDbType {
	case "mongo":
		pool, err := monPools.Get(param.idpDbAddr)
		if err != nil {
			return erro.Wrap(err)
		}
		idpDb = idpdb.NewMongoDb(pool, param.idpDbTag, param.idpDbTag2, webDb)
		log.Info("Use IdP info in mongodb " + param.idpDbAddr + "<" + param.idpDbTag + "." + param.idpDbTag2 + ">")
	default:
		return erro.New("invalid IdP DB type " + param.idpDbType)
	}

	// TA 情報。
	var taDb tadb.Db
	switch param.taDbType {
	case "mongo":
		pool, err := monPools.Get(param.taDbAddr)
		if err != nil {
			return erro.Wrap(err)
		}
		taDb = tadb.NewMongoDb(pool, param.taDbTag, param.taDbTag2, webDb)
		log.Info("Use TA info in mongodb " + param.taDbAddr + "<" + param.taDbTag + "." + param.taDbTag2 + ">")
	default:
		return erro.New("invalid TA DB type " + param.taDbType)
	}

	// セッション。
	var sessDb session.Db
	switch param.sessDbType {
	case "memory":
		sessDb = session.NewMemoryDb()
		log.Info("Save sessions in memory")
	case "redis":
		sessDb = session.NewRedisDb(redPools.Get(param.sessDbAddr), param.sessDbTag)
		log.Info("Save sessions in redis " + param.sessDbAddr + "<" + param.sessDbTag + ">")
	default:
		return erro.New("invalid session DB type " + param.sessDbType)
	}

	var errTmpl *template.Template
	if param.tmplErr != "" {
		errTmpl, err = template.ParseFiles(param.tmplErr)
		if err != nil {
			return erro.Wrap(err)
		}
	}

	idGen := rand.New(time.Minute)

	// バックエンドの準備完了。

	if param.debug {
		idperr.Debug = true
	}

	selPage := idpselect.New(
		stopper,
		param.pathSelUi,
		errTmpl,
		param.sessLabel,
		param.sessLen,
		param.sessExpIn,
		param.sessRefDelay,
		param.sessDbExpIn,
		param.ticLen,
		param.ticExpIn,
		idpDb,
		taDb,
		sessDb,
		idGen,
		param.cookPath,
		param.cookSec,
		param.debug,
	)

	mux := http.NewServeMux()
	routes := map[string]bool{}
	mux.HandleFunc(param.pathOk, idperr.WrapPage(stopper, func(w http.ResponseWriter, r *http.Request) error {
		return nil
	}, errTmpl))
	routes[param.pathOk] = true
	mux.HandleFunc(param.pathStart, selPage.HandleStart)
	routes[param.pathStart] = true
	mux.HandleFunc(param.pathSel, selPage.HandleSelect)
	routes[param.pathSel] = true
	mux.Handle(param.pathIdp, idpapi.New(
		stopper,
		idpDb,
		param.debug,
	))
	routes[param.pathIdp] = true
	if param.uiDir != "" {
		// ファイル配信も自前でやる。
		pathUi := strings.TrimRight(param.pathUi, "/") + "/"
		mux.Handle(pathUi, http.StripPrefix(pathUi, http.FileServer(http.Dir(param.uiDir))))
		routes[param.pathUi] = true
	}

	if !routes["/"] {
		mux.HandleFunc("/", idperr.WrapPage(stopper, func(w http.ResponseWriter, r *http.Request) error {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "invalid endpoint", http.StatusNotFound, nil))
		}, errTmpl))
	}

	// サーバー設定完了。

	defer func() {
		// 処理の終了待ち。
		stopper.Lock()
		defer stopper.Unlock()
		for stopper.Stopped() {
			stopper.Wait()
		}
	}()
	return server.Serve(param, mux)
}
<|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, 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.HandleReadDirAller(&Dir{})

func (d *Dir) ReadDirAll(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
}
<commit_msg>FUSE API change: Attr takes a pointer to fill<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, a *fuse.Attr) {
	a.Size = f.UncompressedSize64
	a.Mode = f.Mode()
	a.Mtime = f.ModTime()
	a.Ctime = f.ModTime()
	a.Crtime = f.ModTime()
}

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

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.HandleReadDirAller(&Dir{})

func (d *Dir) ReadDirAll(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(a *fuse.Attr) {
	zipAttr(f.file, a)
}

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>package main

import (
	"context"
	"flag"
	"net/http"
	"os"
	"os/signal"
	"path"
	"syscall"
	"time"

	"github.com/pkg/errors"
	"github.com/pkg/profile"
	"github.com/sirupsen/logrus"
	"github.com/verath/archipelago/lib"
)

const (
	httpReadTimeout  = 10 * time.Second
	httpWriteTimeout = 10 * time.Second
)

func main() {
	var (
		debug       bool
		serveStatic bool
		serverAddr  string
		profileMode string
	)
	flag.BoolVar(&debug, "debug", false, "Set to true to log debug messages.")
	flag.BoolVar(&serveStatic, "servestatic", false, "Enable serving of static assets.")
	flag.StringVar(&serverAddr, "addr", ":8080", "TCP address for the http server to listen on.")
	flag.StringVar(&profileMode, "profile", "", "Enable profiling mode, one of [cpu, mem, mutex, block]")
	flag.Parse()

	logger := logrus.New()
	logger.Formatter = &logrus.TextFormatter{}
	if debug {
		logger.Level = logrus.DebugLevel
		logger.Info("Debug logging enabled")
	}

	if profileMode != "" {
		defer startProfiling(profileMode)()
	}

	archipelagoServer, err := archipelago.New(logger)
	if err != nil {
		logger.Fatalf("Error creating game: %+v", err)
	}

	http.Handle("/ws", archipelagoServer.WebsocketHandler())
	http.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("OK"))
	})
	if serveStatic {
		staticPath := "./web/dist"
		// Explicitly set a max-age of 0 for service worker script.
		http.HandleFunc("/sw.js", func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Cache-Control", "max-age=0")
			http.ServeFile(w, r, path.Join(staticPath, "sw.js"))
		})
		http.Handle("/", http.FileServer(http.Dir(staticPath)))
	}
	httpServer := &http.Server{
		Addr:         serverAddr,
		ReadTimeout:  httpReadTimeout,
		WriteTimeout: httpWriteTimeout,
	}

	ctx, cancel := context.WithCancel(lifetimeContext(logger))
	errCh := make(chan error)
	go func() { errCh <- archipelagoServer.Run(ctx) }()
	go func() { errCh <- runHTTPServer(ctx, httpServer) }()
	logger.Infof("Archipelago backend started: '%s'", serverAddr)
	err = <-errCh
	cancel()
	<-errCh
	if errors.Cause(err) == context.Canceled {
		logger.Debugf("Error caught in main: %+v", err)
	} else {
		logger.Fatalf("Error caught in main: %+v", err)
	}
	logger.Info("Archipelago backend stopped.")
}

// startProfiling starts profiling for the given profileMode. See
// pkg/profile. Returns a function that should be called when profiling
// should be stopped.
func startProfiling(profileMode string) func() {
	switch profileMode {
	case "cpu":
		return profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.CPUProfile).Stop
	case "mem":
		return profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.MemProfile).Stop
	case "mutex":
		return profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.MutexProfile).Stop
	case "block":
		return profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.BlockProfile).Stop
	default:
		return func() {}
	}
}

// lifetimeContext returns a context that is cancelled on the first SIGINT, SIGTERM,
// or SIGKILL signal received. The application is force closed if more than one
// signal is received.
func lifetimeContext(logger *logrus.Logger) context.Context {
	ctx, cancel := context.WithCancel(context.Background())
	stopSigs := []os.Signal{os.Interrupt, os.Kill, syscall.SIGTERM}
	stopCh := make(chan os.Signal, len(stopSigs))
	signal.Notify(stopCh, stopSigs...)
	go func() {
		<-stopCh
		logger.Info("Caught interrupt, shutting down")
		cancel()
		<-stopCh
		logger.Fatal("Caught second interrupt, force closing")
	}()
	return ctx
}

// runHTTPServer starts and runs the given HTTP server until either an error
// occurs or the context is cancelled.
func runHTTPServer(ctx context.Context, server *http.Server) error {
	errCh := make(chan error)
	go func() { errCh <- server.ListenAndServe() }()
	select {
	case err := <-errCh:
		return err
	case <-ctx.Done():
		server.Close()
		return ctx.Err()
	}
}
<commit_msg>Explicit module name import<commit_after>package main

import (
	"context"
	"flag"
	"net/http"
	"os"
	"os/signal"
	"path"
	"syscall"
	"time"

	"github.com/pkg/errors"
	"github.com/pkg/profile"
	"github.com/sirupsen/logrus"
	archipelago "github.com/verath/archipelago/lib"
)

const (
	httpReadTimeout  = 10 * time.Second
	httpWriteTimeout = 10 * time.Second
)

func main() {
	var (
		debug       bool
		serveStatic bool
		serverAddr  string
		profileMode string
	)
	flag.BoolVar(&debug, "debug", false, "Set to true to log debug messages.")
	flag.BoolVar(&serveStatic, "servestatic", false, "Enable serving of static assets.")
	flag.StringVar(&serverAddr, "addr", ":8080", "TCP address for the http server to listen on.")
	flag.StringVar(&profileMode, "profile", "", "Enable profiling mode, one of [cpu, mem, mutex, block]")
	flag.Parse()

	logger := logrus.New()
	logger.Formatter = &logrus.TextFormatter{}
	if debug {
		logger.Level = logrus.DebugLevel
		logger.Info("Debug logging enabled")
	}

	if profileMode != "" {
		defer startProfiling(profileMode)()
	}

	archipelagoServer, err := archipelago.New(logger)
	if err != nil {
		logger.Fatalf("Error creating game: %+v", err)
	}

	http.Handle("/ws", archipelagoServer.WebsocketHandler())
	http.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("OK"))
	})
	if serveStatic {
		staticPath := "./web/dist"
		// Explicitly set a max-age of 0 for service worker script.
		http.HandleFunc("/sw.js", func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Cache-Control", "max-age=0")
			http.ServeFile(w, r, path.Join(staticPath, "sw.js"))
		})
		http.Handle("/", http.FileServer(http.Dir(staticPath)))
	}
	httpServer := &http.Server{
		Addr:         serverAddr,
		ReadTimeout:  httpReadTimeout,
		WriteTimeout: httpWriteTimeout,
	}

	ctx, cancel := context.WithCancel(lifetimeContext(logger))
	errCh := make(chan error)
	go func() { errCh <- archipelagoServer.Run(ctx) }()
	go func() { errCh <- runHTTPServer(ctx, httpServer) }()
	logger.Infof("Archipelago backend started: '%s'", serverAddr)
	err = <-errCh
	cancel()
	<-errCh
	if errors.Cause(err) == context.Canceled {
		logger.Debugf("Error caught in main: %+v", err)
	} else {
		logger.Fatalf("Error caught in main: %+v", err)
	}
	logger.Info("Archipelago backend stopped.")
}

// startProfiling starts profiling for the given profileMode. See
// pkg/profile. Returns a function that should be called when profiling
// should be stopped.
func startProfiling(profileMode string) func() {
	switch profileMode {
	case "cpu":
		return profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.CPUProfile).Stop
	case "mem":
		return profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.MemProfile).Stop
	case "mutex":
		return profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.MutexProfile).Stop
	case "block":
		return profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.BlockProfile).Stop
	default:
		return func() {}
	}
}

// lifetimeContext returns a context that is cancelled on the first SIGINT, SIGTERM,
// or SIGKILL signal received. The application is force closed if more than one
// signal is received.
func lifetimeContext(logger *logrus.Logger) context.Context {
	ctx, cancel := context.WithCancel(context.Background())
	stopSigs := []os.Signal{os.Interrupt, os.Kill, syscall.SIGTERM}
	stopCh := make(chan os.Signal, len(stopSigs))
	signal.Notify(stopCh, stopSigs...)
	go func() {
		<-stopCh
		logger.Info("Caught interrupt, shutting down")
		cancel()
		<-stopCh
		logger.Fatal("Caught second interrupt, force closing")
	}()
	return ctx
}

// runHTTPServer starts and runs the given HTTP server until either an error
// occurs or the context is cancelled.
func runHTTPServer(ctx context.Context, server *http.Server) error {
	errCh := make(chan error)
	go func() { errCh <- server.ListenAndServe() }()
	select {
	case err := <-errCh:
		return err
	case <-ctx.Done():
		server.Close()
		return ctx.Err()
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc
//
// 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 main

import (
	"compress/gzip"
	"fmt"
	"io"
	"log"
	"mime"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"sync"

	"github.com/drone/drone-plugin-go/plugin"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"

	"google.golang.org/cloud"
	"google.golang.org/cloud/storage"
)

// maxConcurrent is the highest upload concurrency.
const maxConcurrent = 100

var (
	// vargs are provided on stdin of the program
	// and parsed by plugin package.
	vargs struct {
		AuthKey      string            `json:"auth_key"`
		Source       string            `json:"source"`
		Ignore       string            `json:"ignore"`
		Target       string            `json:"target"`
		ACL          []string          `json:"acl"`
		Gzip         []string          `json:"gzip"`
		CacheControl string            `json:"cache_control"`
		Metadata     map[string]string `json:"metadata"`
	}

	// workspace is the repo build workspace.
	workspace plugin.Workspace

	// bucket is the GCS target bucket
	bucket *storage.BucketHandle

	// logging functions
	printf = log.Printf
	fatalf = log.Fatalf

	// program exit code
	ecodeMu sync.Mutex // guards ecode
	ecode   int
)

// errorf sets exit code to a non-zero value and outputs using printf.
func errorf(format string, args ...interface{}) {
	ecodeMu.Lock()
	ecode = 1
	ecodeMu.Unlock()
	printf(format, args...)
}

// result contains upload result of a single file
type result struct {
	name string
	err  error
}

// uploadAll uploads each file sent to channel c in a separate goroutine.
// It reports the upload result to channel r.
// The returned name is a path relative to vargs.Source.
//
// The target object path is created from vargs.Target and the file name
// relative to vargs.Source.
func uploadAll(c <-chan string, r chan<- *result) {
	for f := range c {
		go func(f string) {
			rel, err := filepath.Rel(vargs.Source, f)
			if err != nil {
				r <- &result{f, err}
				return
			}
			err = uploadFile(path.Join(vargs.Target, rel), f)
			r <- &result{rel, err}
		}(f)
	}
}

// uploadFile uploads the file to dst using global bucket.
func uploadFile(dst, file string) error {
	r, gz, err := gzipper(file)
	if err != nil {
		return err
	}
	defer r.Close()
	rel, err := filepath.Rel(vargs.Source, file)
	if err != nil {
		return err
	}
	name := path.Join(vargs.Target, rel)
	w := bucket.Object(name).NewWriter(context.Background())
	w.CacheControl = vargs.CacheControl
	w.Metadata = vargs.Metadata
	for _, s := range vargs.ACL {
		a := strings.SplitN(s, ":", 2)
		if len(a) != 2 {
			return fmt.Errorf("%s: invalid ACL %q", name, s)
		}
		w.ACL = append(w.ACL, storage.ACLRule{
			Entity: storage.ACLEntity(a[0]),
			Role:   storage.ACLRole(a[1]),
		})
	}
	w.ContentType = mime.TypeByExtension(filepath.Ext(file))
	if w.ContentType == "" {
		w.ContentType = "application/octet-stream"
	}
	if gz {
		w.ContentEncoding = "gzip"
	}
	if _, err := io.Copy(w, r); err != nil {
		return err
	}
	// TODO implement exponential backoff
	return w.Close()
}

// gzipper returns a stream of file and a boolean indicating
// whether the stream is gzip-compressed.
//
// The stream is compressed if vargs.Gzip contains file extension.
func gzipper(file string) (io.ReadCloser, bool, error) {
	r, err := os.Open(file)
	if err != nil || !matchGzip(file) {
		return r, false, err
	}
	pr, pw := io.Pipe()
	w := gzip.NewWriter(pw)
	go func() {
		_, err := io.Copy(w, r)
		if err != nil {
			errorf("%s: io.Copy: %v", file, err)
		}
		if err := w.Close(); err != nil {
			errorf("%s: gzip: %v", file, err)
		}
		if err := pw.Close(); err != nil {
			errorf("%s: pipe: %v", file, err)
		}
		r.Close()
	}()
	return pr, true, nil
}

// matchGzip reports whether the file should be gzip-compressed during upload.
// Compressed files should be uploaded with "gzip" content-encoding.
func matchGzip(file string) bool {
	ext := filepath.Ext(file)
	if ext == "" {
		return false
	}
	ext = ext[1:]
	i := sort.SearchStrings(vargs.Gzip, ext)
	return i < len(vargs.Gzip) && vargs.Gzip[i] == ext
}

// walkFiles creates a complete set of files to upload
// by walking vargs.Source recursively.
//
// It excludes files matching vargs.Ignore pattern.
// The ignore pattern is matched using filepath.Match against a partial
// file name, relative to vargs.Source.
func walkFiles() ([]string, error) {
	var items []string
	err := filepath.Walk(vargs.Source, func(p string, fi os.FileInfo, err error) error {
		if err != nil || fi.IsDir() {
			return err
		}
		rel, err := filepath.Rel(vargs.Source, p)
		if err != nil {
			return err
		}
		var ignore bool
		if vargs.Ignore != "" {
			ignore, err = filepath.Match(vargs.Ignore, rel)
		}
		if err != nil || ignore {
			return err
		}
		items = append(items, p)
		return nil
	})
	return items, err
}

// run is the actual entry point called from main.
// It expects vargs and workspace to be initialized
func run(client *storage.Client) {
	// extract bucket name from the target path
	p := strings.SplitN(vargs.Target, "/", 2)
	bname := p[0]
	if len(p) == 1 {
		vargs.Target = ""
	} else {
		vargs.Target = p[1]
	}
	bucket = client.Bucket(strings.Trim(bname, "/"))

	// create a list of files to upload
	vargs.Source = filepath.Join(workspace.Path, vargs.Source)
	src, err := walkFiles()
	if err != nil {
		fatalf("local files: %v", err)
	}

	// upload all files in a goroutine, maxConcurrent at a time
	up := make(chan string, maxConcurrent)
	res := make(chan *result, len(src))
	go uploadAll(up, res)
	for _, f := range src {
		up <- f
	}

	// wait for all files to be uploaded or stop at first error
	for _ = range src {
		r := <-res
		if r.err != nil {
			fatalf("%s: %v", r.name, r.err)
		}
		printf(r.name)
	}
}

func main() {
	log.SetFlags(0)
	plugin.Param("workspace", &workspace)
	plugin.Param("vargs", &vargs)
	plugin.MustParse()
	sort.Strings(vargs.Gzip) // need for matchGzip

	auth, err := google.JWTConfigFromJSON([]byte(vargs.AuthKey), storage.ScopeFullControl)
	if err != nil {
		fatalf("auth: %v", err)
	}
	ctx := context.Background()
	client, err := storage.NewClient(ctx, cloud.WithTokenSource(auth.TokenSource(ctx)))
	if err != nil {
		fatalf("storage client: %v", err)
	}
	run(client)
	os.Exit(ecode)
}
<commit_msg>Actually limit upload concurrency<commit_after>// Copyright 2015 Google Inc
//
// 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 main

import (
	"compress/gzip"
	"fmt"
	"io"
	"log"
	"mime"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"sync"

	"github.com/drone/drone-plugin-go/plugin"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"

	"google.golang.org/cloud"
	"google.golang.org/cloud/storage"
)

// maxConcurrent is the highest upload concurrency.
// It cannot be 0.
const maxConcurrent = 100

var (
	// vargs are provided on stdin of the program
	// and parsed by plugin package.
	vargs struct {
		AuthKey      string            `json:"auth_key"`
		Source       string            `json:"source"`
		Ignore       string            `json:"ignore"`
		Target       string            `json:"target"`
		ACL          []string          `json:"acl"`
		Gzip         []string          `json:"gzip"`
		CacheControl string            `json:"cache_control"`
		Metadata     map[string]string `json:"metadata"`
	}

	// workspace is the repo build workspace.
	workspace plugin.Workspace

	// bucket is the GCS target bucket
	bucket *storage.BucketHandle

	// logging functions
	printf = log.Printf
	fatalf = log.Fatalf

	// program exit code
	ecodeMu sync.Mutex // guards ecode
	ecode   int
)

// errorf sets exit code to a non-zero value and outputs using printf.
func errorf(format string, args ...interface{}) {
	ecodeMu.Lock()
	ecode = 1
	ecodeMu.Unlock()
	printf(format, args...)
}

// uploadFile uploads the file to dst using global bucket.
func uploadFile(dst, file string) error {
	r, gz, err := gzipper(file)
	if err != nil {
		return err
	}
	defer r.Close()
	rel, err := filepath.Rel(vargs.Source, file)
	if err != nil {
		return err
	}
	name := path.Join(vargs.Target, rel)
	w := bucket.Object(name).NewWriter(context.Background())
	w.CacheControl = vargs.CacheControl
	w.Metadata = vargs.Metadata
	for _, s := range vargs.ACL {
		a := strings.SplitN(s, ":", 2)
		if len(a) != 2 {
			return fmt.Errorf("%s: invalid ACL %q", name, s)
		}
		w.ACL = append(w.ACL, storage.ACLRule{
			Entity: storage.ACLEntity(a[0]),
			Role:   storage.ACLRole(a[1]),
		})
	}
	w.ContentType = mime.TypeByExtension(filepath.Ext(file))
	if w.ContentType == "" {
		w.ContentType = "application/octet-stream"
	}
	if gz {
		w.ContentEncoding = "gzip"
	}
	if _, err := io.Copy(w, r); err != nil {
		return err
	}
	// TODO implement exponential backoff
	return w.Close()
}

// gzipper returns a stream of file and a boolean indicating
// whether the stream is gzip-compressed.
//
// The stream is compressed if vargs.Gzip contains file extension.
func gzipper(file string) (io.ReadCloser, bool, error) {
	r, err := os.Open(file)
	if err != nil || !matchGzip(file) {
		return r, false, err
	}
	pr, pw := io.Pipe()
	w := gzip.NewWriter(pw)
	go func() {
		_, err := io.Copy(w, r)
		if err != nil {
			errorf("%s: io.Copy: %v", file, err)
		}
		if err := w.Close(); err != nil {
			errorf("%s: gzip: %v", file, err)
		}
		if err := pw.Close(); err != nil {
			errorf("%s: pipe: %v", file, err)
		}
		r.Close()
	}()
	return pr, true, nil
}

// matchGzip reports whether the file should be gzip-compressed during upload.
// Compressed files should be uploaded with "gzip" content-encoding.
func matchGzip(file string) bool {
	ext := filepath.Ext(file)
	if ext == "" {
		return false
	}
	ext = ext[1:]
	i := sort.SearchStrings(vargs.Gzip, ext)
	return i < len(vargs.Gzip) && vargs.Gzip[i] == ext
}

// walkFiles creates a complete set of files to upload
// by walking vargs.Source recursively.
//
// It excludes files matching vargs.Ignore pattern.
// The ignore pattern is matched using filepath.Match against a partial
// file name, relative to vargs.Source.
func walkFiles() ([]string, error) {
	var items []string
	err := filepath.Walk(vargs.Source, func(p string, fi os.FileInfo, err error) error {
		if err != nil || fi.IsDir() {
			return err
		}
		rel, err := filepath.Rel(vargs.Source, p)
		if err != nil {
			return err
		}
		var ignore bool
		if vargs.Ignore != "" {
			ignore, err = filepath.Match(vargs.Ignore, rel)
		}
		if err != nil || ignore {
			return err
		}
		items = append(items, p)
		return nil
	})
	return items, err
}

// run is the actual entry point called from main.
// It expects vargs and workspace to be initialized
func run(client *storage.Client) {
	// extract bucket name from the target path
	p := strings.SplitN(vargs.Target, "/", 2)
	bname := p[0]
	if len(p) == 1 {
		vargs.Target = ""
	} else {
		vargs.Target = p[1]
	}
	bucket = client.Bucket(strings.Trim(bname, "/"))

	// create a list of files to upload
	vargs.Source = filepath.Join(workspace.Path, vargs.Source)
	src, err := walkFiles()
	if err != nil {
		fatalf("local files: %v", err)
	}

	// result contains upload result of a single file
	type result struct {
		name string
		err  error
	}

	// upload all files in a goroutine, maxConcurrent at a time
	buf := make(chan struct{}, maxConcurrent)
	res := make(chan *result, len(src))
	for _, f := range src {
		buf <- struct{}{} // alloc one slot
		go func(f string) {
			rel, err := filepath.Rel(vargs.Source, f)
			if err != nil {
				res <- &result{f, err}
				return
			}
			err = uploadFile(path.Join(vargs.Target, rel), f)
			res <- &result{rel, err}
			<-buf // free up
		}(f)
	}

	// wait for all files to be uploaded or stop at first error
	for _ = range src {
		r := <-res
		if r.err != nil {
			fatalf("%s: %v", r.name, r.err)
		}
		printf(r.name)
	}
}

func main() {
	log.SetFlags(0)
	plugin.Param("workspace", &workspace)
	plugin.Param("vargs", &vargs)
	plugin.MustParse()
	sort.Strings(vargs.Gzip) // need for matchGzip

	auth, err := google.JWTConfigFromJSON([]byte(vargs.AuthKey), storage.ScopeFullControl)
	if err != nil {
		fatalf("auth: %v", err)
	}
	ctx := context.Background()
	client, err := storage.NewClient(ctx, cloud.WithTokenSource(auth.TokenSource(ctx)))
	if err != nil {
		fatalf("storage client: %v", err)
	}
	run(client)
	os.Exit(ecode)
}
<|endoftext|>