<commit_before>// Copyright 2017 Pilosa Corp.
//
// 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 pql

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

	"github.com/pkg/errors"
)

// timeFormat is the go-style time format used to parse string dates.
const timeFormat = "2006-01-02T15:04"

// error strings in the parser
const duplicateArgErrorMessage = "duplicate argument provided"
const intOutOfRangeError = "integer is not in signed 64-bit range"

// parser represents a parser for the PQL language.
type parser struct {
	r io.Reader
	//scanner *bufScanner
	PQL
}

// NewParser returns a new instance of Parser.
func NewParser(r io.Reader) *parser {
	return &parser{
		r: r,
		//		scanner: newBufScanner(r),
	}
}

// ParseString parses s into a query.
func ParseString(s string) (*Query, error) {
	return NewParser(strings.NewReader(s)).Parse()
}

// Parse parses the next node in the query.
func (p *parser) Parse() (*Query, error) {
	buf, err := ioutil.ReadAll(p.r)
	if err != nil {
		return nil, errors.Wrap(err, "reading buffer to parse")
	}
	p.PQL = PQL{
		Buffer: string(buf),
	}
	p.Init()
	err = p.PQL.Parse()
	if err != nil {
		return nil, errors.Wrap(err, "parsing")
	}

	// Handle specific panics from the parser and return them as errors.
	var v interface{}
	func() {
		defer func() { v = recover() }()
		p.Execute()
	}()
	if v != nil {
		errorMessage := v.(string)
		if strings.HasPrefix(errorMessage, duplicateArgErrorMessage) || strings.HasPrefix(errorMessage, intOutOfRangeError) {
			return nil, fmt.Errorf("%s", v)
		} else {
			panic(v)
		}
	}

	return &p.Query, nil
}
<commit_msg>added check for unexpected parser error<commit_after>// Copyright 2017 Pilosa Corp.
//
// 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 pql

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

	"github.com/pkg/errors"
)

// timeFormat is the go-style time format used to parse string dates.
const timeFormat = "2006-01-02T15:04"

// error strings in the parser
const duplicateArgErrorMessage = "duplicate argument provided"
const intOutOfRangeError = "integer is not in signed 64-bit range"

// parser represents a parser for the PQL language.
type parser struct {
	r io.Reader
	//scanner *bufScanner
	PQL
}

// NewParser returns a new instance of Parser.
func NewParser(r io.Reader) *parser {
	return &parser{
		r: r,
		//		scanner: newBufScanner(r),
	}
}

// ParseString parses s into a query.
func ParseString(s string) (*Query, error) {
	return NewParser(strings.NewReader(s)).Parse()
}

// Parse parses the next node in the query.
func (p *parser) Parse() (*Query, error) {
	buf, err := ioutil.ReadAll(p.r)
	if err != nil {
		return nil, errors.Wrap(err, "reading buffer to parse")
	}
	p.PQL = PQL{
		Buffer: string(buf),
	}
	p.Init()
	err = p.PQL.Parse()
	if err != nil {
		return nil, errors.Wrap(err, "parsing")
	}

	// Handle specific panics from the parser and return them as errors.
	var v interface{}
	func() {
		defer func() { v = recover() }()
		p.Execute()
	}()
	if v != nil {
		errorMessage, ok := v.(string)
		if !ok {
			return nil, fmt.Errorf("unexpected parser error of type %T: %[1]v", v)
		}
		if strings.HasPrefix(errorMessage, duplicateArgErrorMessage) || strings.HasPrefix(errorMessage, intOutOfRangeError) {
			return nil, fmt.Errorf("%s", v)
		} else {
			panic(v)
		}
	}

	return &p.Query, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	telegramBotApi "gopkg.in/telegram-bot-api.v4"
	"log"
	"os"
	"strconv"
)

const UPDATES_TIMEOUT = 60

const COMMAND_ADD = "add"
const COMMAND_LIST = "list"
const COMMAND_CLOSE = "close"

var botApi *telegramBotApi.BotAPI

type Request struct {
	name   string
	closed bool
}

type Bot struct {
	requests []*Request
	botApi   *telegramBotApi.BotAPI
}

func main() {
	bot := getBot()

	for update := range bot.getUpdatesChan() {
		if update.Message == nil {
			continue
		}

		handleUpdate(update, bot)
	}
}

//Logic of messages handling
func handleUpdate(update telegramBotApi.Update, bot *Bot) {
	//log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text)

	message := update.Message

	if message.IsCommand() {
		commandArguments := message.CommandArguments()

		switch command := message.Command(); command {
		case COMMAND_ADD:
			result := bot.addRequest(commandArguments)
			bot.sendReply(update, result)
		case COMMAND_LIST:
			bot.sendReply(update, bot.getRequestsText())
		case COMMAND_CLOSE:
			result := bot.closeRequest(commandArguments)
			bot.sendReply(update, result)
		default:
			bot.sendReply(update, fmt.Sprintf("I can't understart command '%s'", command))
		}
	}
}

func (request Request) String() string {
	return request.name
}

func (bot *Bot) closeRequest(rawRequestNum string) string {
	requestNum, err := strconv.Atoi(rawRequestNum)
	if err != nil {
		return err.Error()
	}

	if requestNum < 0 || requestNum > len(bot.requests) {
		return "Incorrect request number"
	}

	request := bot.requests[requestNum]
	if request.closed {
		return fmt.Sprintf("Request '%s' is already closed", request)
	}

	request.closed = true
	//bot.requests = append(bot.requests[:requestNum], bot.requests[requestNum+1])
	return fmt.Sprintf("Request '%s' closed", request)
}

func (bot *Bot) getRequestsText() string {
	if len(bot.requests) == 0 {
		return "No active requests at the moment"
	}

	var buffer bytes.Buffer

	for number, request := range bot.requests {
		if !request.closed {
			buffer.WriteString(fmt.Sprintf("%d: %s\n", number, request))
		}
	}

	return buffer.String()
}

func getBot() *Bot {
	log.Println("Trying to read 'token' file")
	token := readTokenFile()
	log.Println("Token acquired")

	botApi = getBotApi(token)
	log.Printf("Authorized on account %s", botApi.Self.UserName)

	bot := &Bot{botApi: botApi}
	return bot
}

func (bot *Bot) sendReply(update telegramBotApi.Update, text string) {
	replyMessage := telegramBotApi.NewMessage(update.Message.Chat.ID, text)
	replyMessage.ReplyToMessageID = update.Message.MessageID

	bot.botApi.Send(replyMessage)
}

func (bot *Bot) getUpdatesChan() <-chan telegramBotApi.Update {
	u := telegramBotApi.NewUpdate(0)

	u.Timeout = UPDATES_TIMEOUT

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

	return updates
}

func (bot *Bot) addRequest(name string) string {
	request := &Request{name: name}
	bot.requests = append(bot.requests, request)

	return fmt.Sprintf("Request '%s' added", name)
}

func getBotApi(token string) *telegramBotApi.BotAPI {
	bot, err := telegramBotApi.NewBotAPI(token)
	if err != nil {
		log.Panic(err)
	}

	//bot.Debug = true

	return bot
}

func readTokenFile() string {
	file, err := os.Open("token")
	if err != nil {
		fmt.Println("token file reading error:", err)
		os.Exit(1)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	scanner.Scan()

	return scanner.Text()
}
<commit_msg>input values to be check on sanity<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	telegramBotApi "gopkg.in/telegram-bot-api.v4"
	"log"
	"os"
	"strconv"
)

const UPDATES_TIMEOUT = 60

const COMMAND_ADD = "add"
const COMMAND_LIST = "list"
const COMMAND_CLOSE = "close"

var botApi *telegramBotApi.BotAPI

type Request struct {
	name   string
	closed bool
}

type Bot struct {
	requests []*Request
	botApi   *telegramBotApi.BotAPI
}

func main() {
	bot := getBot()

	for update := range bot.getUpdatesChan() {
		if update.Message == nil {
			continue
		}

		handleUpdate(update, bot)
	}
}

//Logic of messages handling
func handleUpdate(update telegramBotApi.Update, bot *Bot) {
	//log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text)

	message := update.Message

	if message.IsCommand() {
		commandArguments := message.CommandArguments()

		switch command := message.Command(); command {
		case COMMAND_ADD:
			result := bot.addRequest(commandArguments)
			bot.sendReply(update, result)
		case COMMAND_LIST:
			bot.sendReply(update, bot.getRequestsText())
		case COMMAND_CLOSE:
			result := bot.closeRequest(commandArguments)
			bot.sendReply(update, result)
		default:
			bot.sendReply(update, fmt.Sprintf("I can't understart command '%s'", command))
		}
	}
}

func (request Request) String() string {
	return request.name
}

func (bot *Bot) closeRequest(rawRequestNum string) string {
	if len(rawRequestNum) == 0 {
		return "Request number to close required"
	}

	requestNum, err := strconv.Atoi(rawRequestNum)
	if err != nil {
		return fmt.Sprintf("Request number to close required, but got error: %s", err.Error())
	}

	if requestNum < 0 || requestNum > len(bot.requests) {
		return "Incorrect request number"
	}

	request := bot.requests[requestNum]
	if request.closed {
		return fmt.Sprintf("Request '%s' is already closed", request)
	}

	request.closed = true
	//bot.requests = append(bot.requests[:requestNum], bot.requests[requestNum+1])
	return fmt.Sprintf("Request '%s' closed", request)
}

func (bot *Bot) getRequestsText() string {
	if len(bot.requests) == 0 {
		return "No active requests at the moment"
	}

	var buffer bytes.Buffer

	for number, request := range bot.requests {
		if !request.closed {
			buffer.WriteString(fmt.Sprintf("%d: %s\n", number, request))
		}
	}

	return buffer.String()
}

func getBot() *Bot {
	log.Println("Trying to read 'token' file")
	token := readTokenFile()
	log.Println("Token acquired")

	botApi = getBotApi(token)
	log.Printf("Authorized on account %s", botApi.Self.UserName)

	bot := &Bot{botApi: botApi}
	return bot
}

func (bot *Bot) sendReply(update telegramBotApi.Update, text string) {
	replyMessage := telegramBotApi.NewMessage(update.Message.Chat.ID, text)
	replyMessage.ReplyToMessageID = update.Message.MessageID

	bot.botApi.Send(replyMessage)
}

func (bot *Bot) getUpdatesChan() <-chan telegramBotApi.Update {
	u := telegramBotApi.NewUpdate(0)

	u.Timeout = UPDATES_TIMEOUT

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

	return updates
}

func (bot *Bot) addRequest(requestString string) string {
	if len(requestString) == 0 {
		return "Empty request won't be added"
	}

	request := &Request{name: requestString}
	bot.requests = append(bot.requests, request)

	return fmt.Sprintf("Request '%s' added", request)
}

func getBotApi(token string) *telegramBotApi.BotAPI {
	bot, err := telegramBotApi.NewBotAPI(token)
	if err != nil {
		log.Panic(err)
	}

	//bot.Debug = true

	return bot
}

func readTokenFile() string {
	file, err := os.Open("token")
	if err != nil {
		fmt.Println("token file reading error:", err)
		os.Exit(1)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	scanner.Scan()

	return scanner.Text()
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"

	"github.com/julienschmidt/httprouter"
)

type dataBase struct {
	Value []fact `json:"value"`
}

type fact struct {
	ID   float64 `json:"id"`
	Fact string  `json:"fact"`
}

var (
	templates = template.Must(template.ParseFiles("templates/index.tmpl"))
	db        dataBase
)

func main() {
	r := httprouter.New()
	r.ServeFiles("/public/static/*filepath", http.Dir("public/static/"))
	r.GET("/favicon.ico", faviconHandler)
	r.GET("/fact/:id", idHandler)
	r.GET("/api", apiHandler)
	r.GET("/", mainHandler)
	log.Fatal(http.ListenAndServe(":8080", r))
}

func init() {
	db = loadData()
}

func loadData() dataBase {
	data, err := ioutil.ReadFile("data/db.json")
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	err = db.UnmarshalJSON(data)
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	return db
}

func getTheFact(id int) (fact, error) {
	if id == 0 {
		id = rand.Intn(539)
	}

	return db.Value[id], nil
}

func faviconHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	http.ServeFile(w, r, "public/static/favicon.ico")
}

func idHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	id := p.ByName("id")
	if id, err := strconv.Atoi(id); id < 539 && err == nil {
		fact, _ := getTheFact(id)
		templates.ExecuteTemplate(w, "index.tmpl", fact)
	} else {
		http.Error(w, "404 page not found", 404)
	}
}

func mainHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fact, _ := getTheFact(0)
	templates.ExecuteTemplate(w, "index.tmpl", fact)
}

func apiHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fact, _ := getTheFact(0)
	res, err := fact.MarshalJSON()
	if err != nil {
		log.Fatalf("Error: %v", err)
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(res)
}
<commit_msg>Better error handeling<commit_after>package main

import (
	"html/template"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"

	"github.com/julienschmidt/httprouter"
)

type dataBase struct {
	Value []fact `json:"value"`
}

type fact struct {
	ID   float64 `json:"id"`
	Fact string  `json:"fact"`
}

var (
	templates = template.Must(template.ParseFiles("templates/index.tmpl"))
	db        dataBase
)

func main() {
	r := httprouter.New()
	r.ServeFiles("/public/static/*filepath", http.Dir("public/static/"))
	r.GET("/favicon.ico", faviconHandler)
	r.GET("/fact/:id", idHandler)
	r.GET("/api", apiHandler)
	r.GET("/", mainHandler)
	log.Fatal(http.ListenAndServe(":8080", r))
}

func init() {
	db = loadData()
}

func loadData() dataBase {
	data, err := ioutil.ReadFile("data/db.json")
	if err != nil {
		log.Fatalf("Error, loading database: %v", err)
	}

	err = db.UnmarshalJSON(data)
	if err != nil {
		log.Fatalf("Error, parsing database: %v", err)
	}

	return db
}

func getTheFact(id int) (fact, error) {
	if id == 0 {
		id = rand.Intn(539)
	}

	return db.Value[id], nil
}

func faviconHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	http.ServeFile(w, r, "public/static/favicon.ico")
}

func idHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	id := p.ByName("id")
	if id, err := strconv.Atoi(id); id < 539 && err == nil {
		fact, _ := getTheFact(id)
		templates.ExecuteTemplate(w, "index.tmpl", fact)
	} else {
		http.Error(w, "404 page not found", 404)
	}
}

func mainHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fact, _ := getTheFact(0)
	templates.ExecuteTemplate(w, "index.tmpl", fact)
}

func apiHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fact, _ := getTheFact(0)
	res, err := fact.MarshalJSON()
	if err != nil {
		log.Fatalf("Error, marshalling JSON: %v", err)
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(res)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"runtime"

	"github.com/codegangsta/cli"
	"github.com/solderapp/solder-cli/cmd"
)

var (
	version string
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	app := cli.NewApp()
	app.Name = "solder-cli"
	app.Version = version
	app.Author = "Thomas Boerger <thomas@webhippie.de>"
	app.Usage = "Manage mod packs for the Technic launcher"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "server, s",
			Value:  "",
			Usage:  "Solder API server",
			EnvVar: "SOLDER_SERVER",
		},
		cli.StringFlag{
			Name:   "token, t",
			Value:  "",
			Usage:  "Solder API token",
			EnvVar: "SOLDER_TOKEN",
		},
	}

	app.Commands = []cli.Command{
		cmd.Pack(),
		cmd.Build(),
		cmd.Mod(),
		cmd.Version(),
		cmd.Minecraft(),
		cmd.Forge(),
		cmd.User(),
		cmd.Key(),
		cmd.Client(),
		cmd.Profile(),
	}

	cli.HelpFlag = cli.BoolFlag{
		Name:  "help, h",
		Usage: "Show the help, so what you see now",
	}

	cli.VersionFlag = cli.BoolFlag{
		Name:  "version, v",
		Usage: "Print the current version of that tool",
	}

	app.Run(os.Args)
}
<commit_msg>Set basic version<commit_after>package main

import (
	"os"
	"runtime"

	"github.com/codegangsta/cli"
	"github.com/solderapp/solder-cli/cmd"
)

var (
	version string = "0.0.0-dev"
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	app := cli.NewApp()
	app.Name = "solder-cli"
	app.Version = version
	app.Author = "Thomas Boerger <thomas@webhippie.de>"
	app.Usage = "Manage mod packs for the Technic launcher"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "server, s",
			Value:  "",
			Usage:  "Solder API server",
			EnvVar: "SOLDER_SERVER",
		},
		cli.StringFlag{
			Name:   "token, t",
			Value:  "",
			Usage:  "Solder API token",
			EnvVar: "SOLDER_TOKEN",
		},
	}

	app.Commands = []cli.Command{
		cmd.Pack(),
		cmd.Build(),
		cmd.Mod(),
		cmd.Version(),
		cmd.Minecraft(),
		cmd.Forge(),
		cmd.User(),
		cmd.Key(),
		cmd.Client(),
		cmd.Profile(),
	}

	cli.HelpFlag = cli.BoolFlag{
		Name:  "help, h",
		Usage: "Show the help, so what you see now",
	}

	cli.VersionFlag = cli.BoolFlag{
		Name:  "version, v",
		Usage: "Print the current version of that tool",
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"math/big"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"
)

func main() {
	var fromStr string
	flag.StringVar(&fromStr, "from", "0-1-1", "from date")
	var toStr string
	flag.StringVar(&toStr, "to", "9999-1-1", "to date")
	var cmdProperties bool
	flag.BoolVar(&cmdProperties, "props", false, "show properties")
	var cmdMonthlyExpenses bool
	flag.BoolVar(&cmdMonthlyExpenses, "monthly", false, "show monthly expenses")

	flag.Parse()

	// options
	parseDate := func(str string) time.Time {
		parts := dateSepPattern.Split(str, -1)
		if len(parts) != 3 {
			panic(me(nil, "bad date: %s", str))
		}
		year, err := strconv.Atoi(parts[0])
		ce(err, "parse year: %s", str)
		month, err := strconv.Atoi(parts[1])
		ce(err, "parse month: %s", str)
		day, err := strconv.Atoi(parts[2])
		ce(err, "parse day: %s", str)
		return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local)
	}
	fromTime := parseDate(fromStr).Add(-time.Hour)
	toTime := parseDate(toStr).Add(time.Hour)

	// usage
	args := flag.Args()
	if len(args) < 1 {
		pt("usage: %s [options] <file path>\n", os.Args[0])
		flag.Usage()
		return
	}

	// read ledger file
	ledgerPath := args[0]
	contentBytes, err := ioutil.ReadFile(ledgerPath)
	ce(err, "read ledger")
	content := string(contentBytes)
	content = strings.Replace(content, "\r\n", "\n", -1)
	content = strings.Replace(content, "\r", "\n", -1)

	// parse blocks
	var blocks [][]string
	var block []string
	for _, line := range strings.Split(content, "\n") {
		line = strings.TrimSpace(line)
		if len(line) == 0 {
			if len(block) > 0 {
				blocks = append(blocks, block)
				block = []string{}
			}
		} else {
			block = append(block, line)
		}
	}
	if len(block) > 0 {
		blocks = append(blocks, block)
	}

	// transaction
	type Account struct {
		Name        string
		Subs        map[string]*Account
		Parent      *Account
		Balances    map[string]*big.Rat
		Proportions map[string]*big.Rat
	}
	type Entry struct {
		Account     *Account
		Currency    string
		Amount      *big.Rat
		Description string
	}
	type Transaction struct {
		Year        int
		Month       int
		Day         int
		Time        time.Time
		Description string
		Entries     []*Entry
	}
	var transactions []*Transaction

	rootAccount := &Account{
		Name:        "root",
		Subs:        make(map[string]*Account),
		Parent:      nil,
		Balances:    make(map[string]*big.Rat),
		Proportions: make(map[string]*big.Rat),
	}
	var getAccount func(root *Account, path []string) *Account
	getAccount = func(root *Account, path []string) *Account {
		if len(path) == 0 {
			panic(me(nil, "bad account: %v", path))
		} else if len(path) == 1 {
			name := path[0]
			account, ok := root.Subs[name]
			if ok {
				return account
			}
			account = &Account{
				Name:        name,
				Subs:        make(map[string]*Account),
				Parent:      root,
				Balances:    make(map[string]*big.Rat),
				Proportions: make(map[string]*big.Rat),
			}
			root.Subs[name] = account
			return account
		}
		name := path[0]
		account, ok := root.Subs[name]
		if !ok {
			account = &Account{
				Name:        name,
				Subs:        make(map[string]*Account),
				Parent:      root,
				Balances:    make(map[string]*big.Rat),
				Proportions: make(map[string]*big.Rat),
			}
			root.Subs[name] = account
		}
		return getAccount(account, path[1:])
	}

	// collect transactions
	for _, block := range blocks {
		n := 0
		transaction := new(Transaction)

		// parse
		for _, line := range block {
			if strings.HasPrefix(line, "#") {
				continue
			}
			n++

			if n == 1 {
				// transaction header
				parts := blanksPattern.Split(line, 2)
				if len(parts) != 2 {
					panic(me(nil, "bad transaction header: %s", line))
				}

				dateStr := parts[0]
				dateParts := dateSepPattern.Split(dateStr, -1)
				if len(dateParts) != 3 {
					panic(me(nil, "bad date: %s", line))
				}
				year, err := strconv.Atoi(dateParts[0])
				ce(err, "parse year: %s", line)
				transaction.Year = year
				month, err := strconv.Atoi(dateParts[1])
				ce(err, "parse month: %s", line)
				transaction.Month = month
				day, err := strconv.Atoi(dateParts[2])
				ce(err, "parse day: %s", line)
				transaction.Day = day
				transaction.Time = time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local)

				transaction.Description = parts[1]

			} else {
				// entry
				parts := blanksPattern.Split(line, 3)
				if len(parts) < 2 {
					panic(me(nil, "bad entry: %s", line))
				}
				entry := new(Entry)

				accountStr := parts[0]
				account := getAccount(rootAccount, strings.Split(accountStr, "："))
				entry.Account = account

				currency, runeSize := utf8.DecodeRuneInString(parts[1])
				entry.Currency = string(currency)
				amountStr := parts[1][runeSize:]
				amount := new(big.Rat)
				_, ok := amount.SetString(amountStr)
				if !ok {
					panic(me(nil, "bad amount: %s", amountStr))
				}
				entry.Amount = amount

				if len(parts) > 2 {
					entry.Description = parts[2]
				}

				transaction.Entries = append(transaction.Entries, entry)
			}

		}

		if transaction.Year == 0 {
			// empty
			continue
		}
		t := time.Date(transaction.Year, time.Month(transaction.Month), transaction.Day,
			0, 0, 0, 0, time.Local)
		if t.Before(fromTime) || t.After(toTime) {
			// out of range
			continue
		}

		// check balance
		sum := big.NewRat(0, 1)
		for _, entry := range transaction.Entries {
			sum.Add(sum, entry.Amount)
			// update account balance
			account := entry.Account
			for account != nil {
				balance, ok := account.Balances[entry.Currency]
				if !ok {
					balance = big.NewRat(0, 1)
					account.Balances[entry.Currency] = balance
				}
				balance.Add(balance, entry.Amount)
				account = account.Parent
			}
		}
		if !(sum.Cmp(zeroRat) == 0) {
			panic(me(nil, "not balanced: %s", strings.Join(block, "\n")))
		}

		transactions = append(transactions, transaction)
	}

	// calculate proportions
	var calculateProportion func(*Account)
	calculateProportion = func(account *Account) {
		for _, sub := range account.Subs {
			for currency, balance := range sub.Balances {
				if account.Balances[currency].Sign() != 0 {
					b := big.NewRat(0, 1)
					b.Set(balance)
					sub.Proportions[currency] = b.Quo(balance, account.Balances[currency])
					b.Abs(b)
				}
				calculateProportion(sub)
			}
		}
	}
	calculateProportion(rootAccount)

	// print accounts
	onePercent := big.NewRat(1, 100)
	var printAccount func(account *Account, level int)
	printAccount = func(account *Account, level int) {
		allZero := true
		for _, balance := range account.Balances {
			if balance.Cmp(zeroRat) != 0 {
				allZero = false
				break
			}
		}
		if allZero && account != rootAccount {
			return
		}
		pt("%s%s", strings.Repeat(" │    ", level), account.Name)
		var currencyNames []string
		for name := range account.Balances {
			currencyNames = append(currencyNames, name)
		}
		sort.Strings(currencyNames)
		for _, name := range currencyNames {
			balance := account.Balances[name]
			var proportion string
			if p, ok := account.Proportions[name]; ok && p.Cmp(onePercent) > 0 {
				proportion = " " + p.Mul(p, big.NewRat(100, 1)).FloatString(3) + "%"
			}
			pt(" %s%s%s", name, balance.FloatString(2), proportion)
		}
		pt("\n")

		var subNames []string
		for name := range account.Subs {
			subNames = append(subNames, name)
		}
		digitPattern := regexp.MustCompile(`^[0-9]+$`)
		sort.Slice(subNames, func(i, j int) bool {
			a := account.Subs[subNames[i]]
			b := account.Subs[subNames[j]]
			if len(a.Subs) == 0 &&
				len(b.Subs) == 0 &&
				!digitPattern.MatchString(subNames[i]) && // 月份
				!digitPattern.MatchString(subNames[j]) {
				sumA := big.NewRat(0, 1)
				for _, balance := range a.Balances {
					sumA.Add(sumA, balance)
				}
				sumB := big.NewRat(0, 1)
				for _, balance := range b.Balances {
					sumB.Add(sumB, balance)
				}
				return sumA.Cmp(sumB) > 0
			}
			return subNames[i] < subNames[j]
		})
		for _, name := range subNames {
			printAccount(account.Subs[name], level+1)
		}
	}
	printAccount(rootAccount, 0)

	if cmdProperties {
		accountNames := map[string]bool{
			"书籍":   true,
			"保健品":  true,
			"性用品":  true,
			"消耗品":  true,
			"物品":   true,
			"电子":   true,
			"药物":   true,
			"衣物服饰": true,
		}
		accounts := map[*Account]bool{}
		for name, account := range rootAccount.Subs["支出"].Subs {
			if accountNames[name] {
				accounts[account] = true
			}
		}
		var ts []*Transaction
	loop_t:
		for _, t := range transactions {
			for _, entry := range t.Entries {
				if accounts[entry.Account] {
					ts = append(ts, t)
					continue loop_t
				}
			}
		}
		sort.Slice(ts, func(i, j int) bool {
			return ts[i].Time.Before(ts[j].Time)
		})
		for _, t := range ts {
			pt("%s %s\n", t.Time.Format("01-02"), t.Description)
		}
	}

	if cmdMonthlyExpenses {
		accounts := make(map[*Account]bool)
		for _, account := range rootAccount.Subs["支出"].Subs {
			accounts[account] = true
		}
		entries := make(map[string][]*Entry)
		for _, transaction := range transactions {
			for _, entry := range transaction.Entries {
				if accounts[entry.Account] {
					// is expense
					monthStr := fmt.Sprintf("%04d-%02d", transaction.Year, transaction.Month)
					entries[monthStr] = append(entries[monthStr], entry)
				}
			}
		}
		var months []string
		for month := range entries {
			months = append(months, month)
		}
		sort.Strings(months)
		for _, month := range months {
			es := entries[month]
			sums := make(map[string]*big.Rat)
			for _, entry := range es {
				if sums[entry.Currency] == nil {
					sums[entry.Currency] = big.NewRat(0, 1)
				}
				sums[entry.Currency].Add(sums[entry.Currency], entry.Amount)
			}
			pt("%s", month)
			for cur, sum := range sums {
				pt(" %s%s", cur, sum.FloatString(2))
			}
			pt("\n")
		}
	}

}
<commit_msg>update<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"math/big"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"
)

func main() {
	var fromStr string
	flag.StringVar(&fromStr, "from", "0-1-1", "from date")
	var toStr string
	flag.StringVar(&toStr, "to", "9999-1-1", "to date")
	var cmdProperties bool
	flag.BoolVar(&cmdProperties, "props", false, "show properties")
	var cmdMonthlyExpenses bool
	flag.BoolVar(&cmdMonthlyExpenses, "monthly", false, "show monthly expenses")

	flag.Parse()

	// options
	parseDate := func(str string) time.Time {
		parts := dateSepPattern.Split(str, -1)
		if len(parts) != 3 {
			panic(me(nil, "bad date: %s", str))
		}
		year, err := strconv.Atoi(parts[0])
		ce(err, "parse year: %s", str)
		month, err := strconv.Atoi(parts[1])
		ce(err, "parse month: %s", str)
		day, err := strconv.Atoi(parts[2])
		ce(err, "parse day: %s", str)
		return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local)
	}
	fromTime := parseDate(fromStr).Add(-time.Hour)
	toTime := parseDate(toStr).Add(time.Hour)

	// usage
	args := flag.Args()
	if len(args) < 1 {
		pt("usage: %s [options] <file path>\n", os.Args[0])
		flag.Usage()
		return
	}

	// read ledger file
	ledgerPath := args[0]
	contentBytes, err := ioutil.ReadFile(ledgerPath)
	ce(err, "read ledger")
	content := string(contentBytes)
	content = strings.Replace(content, "\r\n", "\n", -1)
	content = strings.Replace(content, "\r", "\n", -1)

	// parse blocks
	var blocks [][]string
	var block []string
	for _, line := range strings.Split(content, "\n") {
		line = strings.TrimSpace(line)
		if len(line) == 0 {
			if len(block) > 0 {
				blocks = append(blocks, block)
				block = []string{}
			}
		} else {
			block = append(block, line)
		}
	}
	if len(block) > 0 {
		blocks = append(blocks, block)
	}

	// transaction
	type Account struct {
		Name        string
		Subs        map[string]*Account
		Parent      *Account
		Balances    map[string]*big.Rat
		Proportions map[string]*big.Rat
	}
	type Entry struct {
		Account     *Account
		Currency    string
		Amount      *big.Rat
		Description string
	}
	type Transaction struct {
		Year        int
		Month       int
		Day         int
		Time        time.Time
		Description string
		Entries     []*Entry
	}
	var transactions []*Transaction

	rootAccount := &Account{
		Name:        "root",
		Subs:        make(map[string]*Account),
		Parent:      nil,
		Balances:    make(map[string]*big.Rat),
		Proportions: make(map[string]*big.Rat),
	}
	var getAccount func(root *Account, path []string) *Account
	getAccount = func(root *Account, path []string) *Account {
		if len(path) == 0 {
			panic(me(nil, "bad account: %v", path))
		} else if len(path) == 1 {
			name := path[0]
			account, ok := root.Subs[name]
			if ok {
				return account
			}
			account = &Account{
				Name:        name,
				Subs:        make(map[string]*Account),
				Parent:      root,
				Balances:    make(map[string]*big.Rat),
				Proportions: make(map[string]*big.Rat),
			}
			root.Subs[name] = account
			return account
		}
		name := path[0]
		account, ok := root.Subs[name]
		if !ok {
			account = &Account{
				Name:        name,
				Subs:        make(map[string]*Account),
				Parent:      root,
				Balances:    make(map[string]*big.Rat),
				Proportions: make(map[string]*big.Rat),
			}
			root.Subs[name] = account
		}
		return getAccount(account, path[1:])
	}

	// collect transactions
	for _, block := range blocks {
		n := 0
		transaction := new(Transaction)

		// parse
		for _, line := range block {
			if strings.HasPrefix(line, "#") {
				continue
			}
			n++

			if n == 1 {
				// transaction header
				parts := blanksPattern.Split(line, 2)
				if len(parts) != 2 {
					panic(me(nil, "bad transaction header: %s", line))
				}

				dateStr := parts[0]
				dateParts := dateSepPattern.Split(dateStr, -1)
				if len(dateParts) != 3 {
					panic(me(nil, "bad date: %s", line))
				}
				year, err := strconv.Atoi(dateParts[0])
				ce(err, "parse year: %s", line)
				transaction.Year = year
				month, err := strconv.Atoi(dateParts[1])
				ce(err, "parse month: %s", line)
				transaction.Month = month
				day, err := strconv.Atoi(dateParts[2])
				ce(err, "parse day: %s", line)
				transaction.Day = day
				transaction.Time = time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local)

				transaction.Description = parts[1]

			} else {
				// entry
				parts := blanksPattern.Split(line, 3)
				if len(parts) < 2 {
					panic(me(nil, "bad entry: %s", line))
				}
				entry := new(Entry)

				accountStr := parts[0]
				account := getAccount(rootAccount, strings.Split(accountStr, "："))
				entry.Account = account

				currency, runeSize := utf8.DecodeRuneInString(parts[1])
				entry.Currency = string(currency)
				amountStr := parts[1][runeSize:]
				amount := new(big.Rat)
				_, ok := amount.SetString(amountStr)
				if !ok {
					panic(me(nil, "bad amount: %s", amountStr))
				}
				entry.Amount = amount

				if len(parts) > 2 {
					entry.Description = parts[2]
				}

				transaction.Entries = append(transaction.Entries, entry)
			}

		}

		if transaction.Year == 0 {
			// empty
			continue
		}
		t := time.Date(transaction.Year, time.Month(transaction.Month), transaction.Day,
			0, 0, 0, 0, time.Local)
		if t.Before(fromTime) || t.After(toTime) {
			// out of range
			continue
		}

		// check balance
		sum := big.NewRat(0, 1)
		for _, entry := range transaction.Entries {
			sum.Add(sum, entry.Amount)
			// update account balance
			account := entry.Account
			for account != nil {
				balance, ok := account.Balances[entry.Currency]
				if !ok {
					balance = big.NewRat(0, 1)
					account.Balances[entry.Currency] = balance
				}
				balance.Add(balance, entry.Amount)
				account = account.Parent
			}
		}
		if !(sum.Cmp(zeroRat) == 0) {
			panic(me(nil, "not balanced: %s", strings.Join(block, "\n")))
		}

		transactions = append(transactions, transaction)
	}

	// calculate proportions
	var calculateProportion func(*Account)
	calculateProportion = func(account *Account) {
		for _, sub := range account.Subs {
			for currency, balance := range sub.Balances {
				if account.Balances[currency].Sign() != 0 {
					b := big.NewRat(0, 1)
					b.Set(balance)
					sub.Proportions[currency] = b.Quo(balance, account.Balances[currency])
					b.Abs(b)
				}
				calculateProportion(sub)
			}
		}
	}
	calculateProportion(rootAccount)

	// print accounts
	onePercent := big.NewRat(1, 100)
	var printAccount func(account *Account, level int)
	printAccount = func(account *Account, level int) {
		allZero := true
		for _, balance := range account.Balances {
			if balance.Cmp(zeroRat) != 0 {
				allZero = false
				break
			}
		}
		if allZero && account != rootAccount {
			return
		}
		pt("%s%s", strings.Repeat(" │    ", level), account.Name)
		var currencyNames []string
		for name := range account.Balances {
			currencyNames = append(currencyNames, name)
		}
		sort.Strings(currencyNames)
		for _, name := range currencyNames {
			balance := account.Balances[name]
			var proportion string
			if p, ok := account.Proportions[name]; ok && p.Cmp(onePercent) > 0 {
				proportion = " " + p.Mul(p, big.NewRat(100, 1)).FloatString(3) + "%"
			}
			pt(" %s%s%s", name, balance.FloatString(2), proportion)
		}
		pt("\n")

		var subNames []string
		for name := range account.Subs {
			subNames = append(subNames, name)
		}
		digitPattern := regexp.MustCompile(`^[0-9]+$`)
		sort.Slice(subNames, func(i, j int) bool {
			a := account.Subs[subNames[i]]
			b := account.Subs[subNames[j]]
			if len(a.Subs) == 0 &&
				len(b.Subs) == 0 &&
				!digitPattern.MatchString(subNames[i]) && // 月份
				!digitPattern.MatchString(subNames[j]) {
				sumA := big.NewRat(0, 1)
				for _, balance := range a.Balances {
					sumA.Add(sumA, balance)
				}
				sumB := big.NewRat(0, 1)
				for _, balance := range b.Balances {
					sumB.Add(sumB, balance)
				}
				return sumA.Cmp(sumB) > 0
			}
			return subNames[i] < subNames[j]
		})
		for _, name := range subNames {
			printAccount(account.Subs[name], level+1)
		}
	}
	printAccount(rootAccount, 0)

	if cmdProperties {
		accountNames := map[string]bool{
			"书籍":   true,
			"保健品":  true,
			"性用品":  true,
			"消耗品":  true,
			"物品":   true,
			"电子":   true,
			"药物":   true,
			"衣物服饰": true,
		}
		accounts := map[*Account]bool{}
		for name, account := range rootAccount.Subs["支出"].Subs {
			if accountNames[name] {
				accounts[account] = true
			}
		}
		var ts []*Transaction
	loop_t:
		for _, t := range transactions {
			for _, entry := range t.Entries {
				if accounts[entry.Account] {
					ts = append(ts, t)
					continue loop_t
				}
			}
		}
		sort.Slice(ts, func(i, j int) bool {
			return ts[i].Time.Before(ts[j].Time)
		})
		for _, t := range ts {
			pt("%s %s\n", t.Time.Format("01-02"), t.Description)
		}
	}

	if cmdMonthlyExpenses {
		accounts := make(map[*Account]bool)
		for _, account := range rootAccount.Subs["支出"].Subs {
			accounts[account] = true
		}
		monthEntries := make(map[string][]*Entry)
		for _, transaction := range transactions {
			for _, entry := range transaction.Entries {
				if accounts[entry.Account] {
					// is expense
					monthStr := fmt.Sprintf("%04d-%02d", transaction.Year, transaction.Month)
					monthEntries[monthStr] = append(monthEntries[monthStr], entry)
				}
			}
		}
		var months []string
		for month := range monthEntries {
			months = append(months, month)
		}
		sort.Strings(months)
		for _, month := range months {
			entries := monthEntries[month]
			sums := make(map[string]*big.Rat)
			for _, entry := range entries {
				if sums[entry.Currency] == nil {
					sums[entry.Currency] = big.NewRat(0, 1)
				}
				sums[entry.Currency].Add(sums[entry.Currency], entry.Amount)
			}
			pt("%s", month)
			for cur, sum := range sums {
				pt(" %s%s", cur, sum.FloatString(2))
			}
			pt("\n")
		}
	}

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"time"

	"github.com/paypal/gatt"
	"github.com/paypal/gatt/linux/cmd"
	"github.com/stianeikeland/go-rpio"
)

var (
	current = 75
	desired = 75
	fanmode = "auto"
	sysmode = "off"

	fan  = rpio.Pin(17)
	cool = rpio.Pin(21)
	heat = rpio.Pin(22)

	exiting      = false
	disconnected = make(chan bool)
	peripheral   gatt.Peripheral

	DefaultClientOptions = []gatt.Option{
		gatt.LnxMaxConnections(1),
		gatt.LnxDeviceID(-1, true),
	}

	DefaultServerOptions = []gatt.Option{
		gatt.LnxMaxConnections(1),
		gatt.LnxDeviceID(-1, true),
		gatt.LnxSetAdvertisingParameters(&cmd.LESetAdvertisingParameters{
			AdvertisingIntervalMin: 0x00f4,
			AdvertisingIntervalMax: 0x00f4,
			AdvertisingChannelMap:  0x7,
		}),
	}
)

// API

type Thermostat struct {
	Current int    `json:"current"`
	Desired int    `json:"desired"`
	Sysmode string `json:"sysmode"`
	Fanmode string `json:"fanmode"`
}

func updateHandler(r *http.Request) int {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return http.StatusBadRequest
	}

	var t Thermostat
	err = json.Unmarshal(body, &t)
	if err != nil {
		return http.StatusBadRequest
	}

	if t.Desired >= 60 && t.Desired <= 85 {
		desired = t.Desired
	}
	if t.Current >= 50 && t.Current <= 90 {
		current = t.Current
	}
	sysmode = t.Sysmode
	fanmode = t.Fanmode
	updateState()
	return http.StatusOK
}

func apiHandler(w http.ResponseWriter, r *http.Request) {
	var code int

	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Headers", "Content-Type")

	switch r.Method {
	case "GET":
		code = http.StatusOK
	case "POST":
		code = updateHandler(r)
	case "OPTIONS":
		w.Header().Set("Allow", "GET,POST,OPTIONS")
		return
	default:
		code = http.StatusNotImplemented
	}
	log.Printf("current: %d, desired: %d, sysmode: %s, fanmode: %s\n", current, desired, sysmode, fanmode)
	response := Thermostat{current, desired, sysmode, fanmode}
	json, err := json.Marshal(response)
	if err != nil {
		http.Error(w, "Error marshalling JSON", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	if code != 200 {
		http.Error(w, "", code)
	}
	w.Write(json)
	log.Printf("%s %s %s %d", r.RemoteAddr, r.Method, r.URL, 200)
}

// GPIO

func Start(p rpio.Pin) {
	p.Low()
}

func Stop(p rpio.Pin) {
	p.High()
}

// Sensor

func onStateChanged(d gatt.Device, s gatt.State) {
	log.Println("State:", s)
	switch s {
	case gatt.StatePoweredOn:
		log.Println("Scanning...")
		d.Scan([]gatt.UUID{}, false)
		return
	default:
		d.StopScanning()
	}
}

func onPeriphDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
	if !strings.Contains(a.LocalName, "SensorTag") {
		return
	}

	// Stop scanning once we've got the peripheral we're looking for.
	p.Device().StopScanning()

	log.Printf("\nPeripheral ID:%s, NAME:(%s)\n", p.ID(), p.Name())
	log.Println("  Local Name        =", a.LocalName)
	log.Println("  TX Power Level    =", a.TxPowerLevel)
	log.Println("  Manufacturer Data =", a.ManufacturerData)
	log.Println("  Service Data      =", a.ServiceData)
	log.Println("")

	p.Device().Connect(p)
}

func convertTemp(b []byte) int {
	rawdata := binary.LittleEndian.Uint16(b)
	return int((float32(rawdata)/4.0)*float32(0.03125)*(9.0/5.0) + 32)
}

func handleTempNotification(c *gatt.Characteristic, b []byte, err error) {
	t := convertTemp(b[2:4])
	if t > 50 && t < 90 { // sanity range
		current = t
		updateState()
	}
}

func onPeriphConnected(p gatt.Peripheral, err error) {
	log.Println("Connected")
	peripheral = p

	err = p.SetMTU(500)
	if err != nil {
		log.Printf("Failed to set MTU, err: %s\n", err)
	}

	uuid, err := gatt.ParseUUID("f000aa00-0451-4000-b000-000000000000")
	if err != nil {
		log.Printf("Failed to create UUID, err: %s\n", err)
		return
	}

	ss, err := p.DiscoverServices(nil)
	if err != nil {
		log.Printf("Failed to discover IR sensor service, err: %s\n", err)
		return
	}

	var s *gatt.Service
	for _, s = range ss {
		if s.UUID().Equal(uuid) {
			break
		}
	}

	cs, err := p.DiscoverCharacteristics(nil, s)
	if err != nil {
		log.Printf("Failed to discover characteristics, err: %s\n", err)
		return
	}

	for _, c := range cs {
		_, err = p.DiscoverDescriptors(nil, c)
		if err != nil {
			log.Printf("Failed to discover descriptors, err: %s\n", err)
			return
		}
	}

	data := cs[0]
	config := cs[1]
	samplerate := cs[2]

	// Turn on notifications
	err = p.SetNotifyValue(data, handleTempNotification)
	if err != nil {
		log.Printf("Failed to write config characteristics, err: %s\n", err)
		return
	}

	// Set sample rate
	err = p.WriteCharacteristic(samplerate, []byte{0xFF}, false)
	if err != nil {
		log.Printf("Failed to write samplerate characteristics, err: %s\n", err)
		return
	}

	// Start sensors
	err = p.WriteCharacteristic(config, []byte{0x01}, false)
	if err != nil {
		log.Printf("Failed to write config characteristics, err: %s\n", err)
		return
	}
}

func onPeriphDisconnected(p gatt.Peripheral, err error) {
	if exiting {
		disconnected <- true
		return
	}
	current = 0
	updateState()
	peripheral = nil
	log.Println("Connection lost, resuming scanning...")
	p.Device().Scan([]gatt.UUID{}, false)
}

func updateState() {
	if current == 0 {
		Stop(fan)
		Stop(cool)
		Stop(heat)
		return
	}
	switch sysmode {
	case "off":
		Stop(heat)
		Stop(cool)
		if fanmode == "auto" {
			Stop(fan)
		}
	case "cool":
		if current > desired {
			Start(fan)
			Start(cool)
		} else if current < desired {
			if fanmode == "auto" {
				Stop(fan)
			}
			Stop(cool)
		}
		Stop(heat)
	case "heat":
		if current < desired {
			Start(fan)
			Start(heat)
		} else if current > desired {
			if fanmode == "auto" {
				Stop(fan)
			}
			Stop(heat)
		}
		Stop(cool)
	}

	if fanmode == "on" {
		Start(fan)
	}
	go func() {
		led := rpio.Pin(16)
		led.Output()
		led.Low()
		<-time.After(100 * time.Millisecond)
		led.High()
	}()
}

func main() {
	// Sensor
	d, err := gatt.NewDevice(DefaultClientOptions...)
	if err != nil {
		log.Fatalf("Failed to open device, err: %s\n", err)
		return
	}

	d.Handle(
		gatt.PeripheralDiscovered(onPeriphDiscovered),
		gatt.PeripheralConnected(onPeriphConnected),
		gatt.PeripheralDisconnected(onPeriphDisconnected),
	)

	d.Init(onStateChanged)

	// API
	http.HandleFunc("/api", apiHandler)
	http.Handle("/", http.FileServer(http.Dir("./ui")))
	go http.ListenAndServe(":80", nil)

	// GPIO
	err = rpio.Open()
	if err != nil {
		log.Printf("Failed to open controller, err: %s\n", err)
		return
	}
	defer func() {
		for _, p := range []rpio.Pin{fan, cool, heat} {
			Stop(p)
		}
		rpio.Close()
	}()

	for _, p := range []rpio.Pin{fan, cool, heat} {
		p.Output()
		Stop(p)
	}

	done := make(chan bool)
	go func() {
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt)
		<-c
		done <- true
	}()
	<-done

	exiting = true
	if peripheral != nil {
		d.CancelConnection(peripheral)
		<-disconnected
	}
}
<commit_msg>disable CORS<commit_after>package main

import (
	"encoding/binary"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"time"

	"github.com/paypal/gatt"
	"github.com/paypal/gatt/linux/cmd"
	"github.com/stianeikeland/go-rpio"
)

var (
	current = 75
	desired = 75
	fanmode = "auto"
	sysmode = "off"

	fan  = rpio.Pin(17)
	cool = rpio.Pin(21)
	heat = rpio.Pin(22)

	exiting      = false
	disconnected = make(chan bool)
	peripheral   gatt.Peripheral

	DefaultClientOptions = []gatt.Option{
		gatt.LnxMaxConnections(1),
		gatt.LnxDeviceID(-1, true),
	}

	DefaultServerOptions = []gatt.Option{
		gatt.LnxMaxConnections(1),
		gatt.LnxDeviceID(-1, true),
		gatt.LnxSetAdvertisingParameters(&cmd.LESetAdvertisingParameters{
			AdvertisingIntervalMin: 0x00f4,
			AdvertisingIntervalMax: 0x00f4,
			AdvertisingChannelMap:  0x7,
		}),
	}
)

// API

type Thermostat struct {
	Current int    `json:"current"`
	Desired int    `json:"desired"`
	Sysmode string `json:"sysmode"`
	Fanmode string `json:"fanmode"`
}

func updateHandler(r *http.Request) int {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return http.StatusBadRequest
	}

	var t Thermostat
	err = json.Unmarshal(body, &t)
	if err != nil {
		return http.StatusBadRequest
	}

	if t.Desired >= 60 && t.Desired <= 85 {
		desired = t.Desired
	}
	if t.Current >= 50 && t.Current <= 90 {
		current = t.Current
	}
	sysmode = t.Sysmode
	fanmode = t.Fanmode
	updateState()
	return http.StatusOK
}

func apiHandler(w http.ResponseWriter, r *http.Request) {
	var code int

	switch r.Method {
	case "GET":
		code = http.StatusOK
	case "POST":
		code = updateHandler(r)
	default:
		code = http.StatusNotImplemented
	}
	log.Printf("current: %d, desired: %d, sysmode: %s, fanmode: %s\n", current, desired, sysmode, fanmode)
	response := Thermostat{current, desired, sysmode, fanmode}
	json, err := json.Marshal(response)
	if err != nil {
		http.Error(w, "Error marshalling JSON", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	if code != 200 {
		http.Error(w, "", code)
	}
	w.Write(json)
	log.Printf("%s %s %s %d", r.RemoteAddr, r.Method, r.URL, 200)
}

// GPIO

func Start(p rpio.Pin) {
	p.Low()
}

func Stop(p rpio.Pin) {
	p.High()
}

// Sensor

func onStateChanged(d gatt.Device, s gatt.State) {
	log.Println("State:", s)
	switch s {
	case gatt.StatePoweredOn:
		log.Println("Scanning...")
		d.Scan([]gatt.UUID{}, false)
		return
	default:
		d.StopScanning()
	}
}

func onPeriphDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
	if !strings.Contains(a.LocalName, "SensorTag") {
		return
	}

	// Stop scanning once we've got the peripheral we're looking for.
	p.Device().StopScanning()

	log.Printf("\nPeripheral ID:%s, NAME:(%s)\n", p.ID(), p.Name())
	log.Println("  Local Name        =", a.LocalName)
	log.Println("  TX Power Level    =", a.TxPowerLevel)
	log.Println("  Manufacturer Data =", a.ManufacturerData)
	log.Println("  Service Data      =", a.ServiceData)
	log.Println("")

	p.Device().Connect(p)
}

func convertTemp(b []byte) int {
	rawdata := binary.LittleEndian.Uint16(b)
	return int((float32(rawdata)/4.0)*float32(0.03125)*(9.0/5.0) + 32)
}

func handleTempNotification(c *gatt.Characteristic, b []byte, err error) {
	t := convertTemp(b[2:4])
	if t > 50 && t < 90 { // sanity range
		current = t
		updateState()
	}
}

func onPeriphConnected(p gatt.Peripheral, err error) {
	log.Println("Connected")
	peripheral = p

	err = p.SetMTU(500)
	if err != nil {
		log.Printf("Failed to set MTU, err: %s\n", err)
	}

	uuid, err := gatt.ParseUUID("f000aa00-0451-4000-b000-000000000000")
	if err != nil {
		log.Printf("Failed to create UUID, err: %s\n", err)
		return
	}

	ss, err := p.DiscoverServices(nil)
	if err != nil {
		log.Printf("Failed to discover IR sensor service, err: %s\n", err)
		return
	}

	var s *gatt.Service
	for _, s = range ss {
		if s.UUID().Equal(uuid) {
			break
		}
	}

	cs, err := p.DiscoverCharacteristics(nil, s)
	if err != nil {
		log.Printf("Failed to discover characteristics, err: %s\n", err)
		return
	}

	for _, c := range cs {
		_, err = p.DiscoverDescriptors(nil, c)
		if err != nil {
			log.Printf("Failed to discover descriptors, err: %s\n", err)
			return
		}
	}

	data := cs[0]
	config := cs[1]
	samplerate := cs[2]

	// Turn on notifications
	err = p.SetNotifyValue(data, handleTempNotification)
	if err != nil {
		log.Printf("Failed to write config characteristics, err: %s\n", err)
		return
	}

	// Set sample rate
	err = p.WriteCharacteristic(samplerate, []byte{0xFF}, false)
	if err != nil {
		log.Printf("Failed to write samplerate characteristics, err: %s\n", err)
		return
	}

	// Start sensors
	err = p.WriteCharacteristic(config, []byte{0x01}, false)
	if err != nil {
		log.Printf("Failed to write config characteristics, err: %s\n", err)
		return
	}
}

func onPeriphDisconnected(p gatt.Peripheral, err error) {
	if exiting {
		disconnected <- true
		return
	}
	current = 0
	updateState()
	peripheral = nil
	log.Println("Connection lost, resuming scanning...")
	p.Device().Scan([]gatt.UUID{}, false)
}

func updateState() {
	if current == 0 {
		Stop(fan)
		Stop(cool)
		Stop(heat)
		return
	}
	switch sysmode {
	case "off":
		Stop(heat)
		Stop(cool)
		if fanmode == "auto" {
			Stop(fan)
		}
	case "cool":
		if current > desired {
			Start(fan)
			Start(cool)
		} else if current < desired {
			if fanmode == "auto" {
				Stop(fan)
			}
			Stop(cool)
		}
		Stop(heat)
	case "heat":
		if current < desired {
			Start(fan)
			Start(heat)
		} else if current > desired {
			if fanmode == "auto" {
				Stop(fan)
			}
			Stop(heat)
		}
		Stop(cool)
	}

	if fanmode == "on" {
		Start(fan)
	}
	go func() {
		led := rpio.Pin(16)
		led.Output()
		led.Low()
		<-time.After(100 * time.Millisecond)
		led.High()
	}()
}

func main() {
	// Sensor
	d, err := gatt.NewDevice(DefaultClientOptions...)
	if err != nil {
		log.Fatalf("Failed to open device, err: %s\n", err)
		return
	}

	d.Handle(
		gatt.PeripheralDiscovered(onPeriphDiscovered),
		gatt.PeripheralConnected(onPeriphConnected),
		gatt.PeripheralDisconnected(onPeriphDisconnected),
	)

	d.Init(onStateChanged)

	// API
	http.HandleFunc("/api", apiHandler)
	http.Handle("/", http.FileServer(http.Dir("./ui")))
	go http.ListenAndServe(":80", nil)

	// GPIO
	err = rpio.Open()
	if err != nil {
		log.Printf("Failed to open controller, err: %s\n", err)
		return
	}
	defer func() {
		for _, p := range []rpio.Pin{fan, cool, heat} {
			Stop(p)
		}
		rpio.Close()
	}()

	for _, p := range []rpio.Pin{fan, cool, heat} {
		p.Output()
		Stop(p)
	}

	done := make(chan bool)
	go func() {
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt)
		<-c
		done <- true
	}()
	<-done

	exiting = true
	if peripheral != nil {
		d.CancelConnection(peripheral)
		<-disconnected
	}
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "errors"
    "bytes"
    "io/ioutil"
    "bufio"
    "os"
    "strings"
    "regexp"
    flags "github.com/jessevdk/go-flags"
    "github.com/remeh/sizedwaitgroup"
)

const (
    Author  = "webdevops.io"
    Version = "1.1.1"
)

type changeset struct {
    SearchPlain string
    Search      *regexp.Regexp
    Replace     string
    MatchFound  bool
}

type changeresult struct {
    File   fileitem
    Output string
    Status bool
    Error  error
}

type fileitem struct {
    Path        string
    Output      string
}

var opts struct {
    ThreadCount             int      `           long:"threads"                       description:"Set thread concurrency for replacing in multiple files at same time" default:"10"`
    Mode                    string   `short:"m"  long:"mode"                          description:"replacement mode - replace: replace match with term; line: replace line with term; lineinfile: replace line with term or if not found append to term to file; template: parse content as golang template, search value have to start uppercase" default:"replace" choice:"replace" choice:"line" choice:"lineinfile" choice:"template"`
    ModeIsReplaceMatch      bool
    ModeIsReplaceLine       bool
    ModeIsLineInFile        bool
    ModeIsTemplate          bool
    Search                  []string `short:"s"  long:"search"                        description:"search term"`
    Replace                 []string `short:"r"  long:"replace"                       description:"replacement term"`
    LineinfileBefore        string   `           long:"lineinfile-before"             description:"add line before this regex"`
    LineinfileAfter         string   `           long:"lineinfile-after"              description:"add line after this regex"`
    CaseInsensitive         bool     `short:"i"  long:"case-insensitive"              description:"ignore case of pattern to match upper and lowercase characters"`
    Stdin                   bool     `           long:"stdin"                         description:"process stdin as input"`
    Output                  string   `short:"o"  long:"output"                        description:"write changes to this file (in one file mode)"`
    OutputStripFileExt      string   `           long:"output-strip-ext"              description:"strip file extension from written files (also available in multi file mode)"`
    Once                    string   `           long:"once"                          description:"replace search term only one in a file, keep duplicaes (keep, default) or remove them (unique)" optional:"true" optional-value:"keep" choice:"keep" choice:"unique"`
    Regex                   bool     `           long:"regex"                         description:"treat pattern as regex"`
    RegexBackref            bool     `           long:"regex-backrefs"                description:"enable backreferences in replace term"`
    RegexPosix              bool     `           long:"regex-posix"                   description:"parse regex term as POSIX regex"`
    Path                    string   `           long:"path"                          description:"use files in this path"`
    PathPattern             string   `           long:"path-pattern"                  description:"file pattern (* for wildcard, only basename of file)"`
    PathRegex               string   `           long:"path-regex"                    description:"file pattern (regex, full path)"`
    IgnoreEmpty             bool     `           long:"ignore-empty"                  description:"ignore empty file list, otherwise this will result in an error"`
    Verbose                 bool     `short:"v"  long:"verbose"                       description:"verbose mode"`
    DryRun                  bool     `           long:"dry-run"                       description:"dry run mode"`
    ShowVersion             bool     `short:"V"  long:"version"                       description:"show version and exit"`
    ShowOnlyVersion         bool     `           long:"dumpversion"                   description:"show only version number and exit"`
    ShowHelp                bool     `short:"h"  long:"help"                          description:"show this help message"`
}

var pathFilterDirectories = []string{"autom4te.cache", "blib", "_build", ".bzr", ".cdv", "cover_db", "CVS", "_darcs", "~.dep", "~.dot", ".git", ".hg", "~.nib", ".pc", "~.plst", "RCS", "SCCS", "_sgbak", ".svn", "_obj", ".idea"}

// Apply changesets to file
func applyChangesetsToFile(fileitem fileitem, changesets []changeset) (string, bool, error) {
    var (
        err error = nil
        output string = ""
        status bool = true
    )

    // try open file
    file, err := os.Open(fileitem.Path)
    if err != nil {
        return output, false, err
    }

    writeBufferToFile := false
    var buffer bytes.Buffer

    r := bufio.NewReader(file)
    line, e := Readln(r)
    for e == nil {
        newLine, lineChanged, skipLine := applyChangesetsToLine(line, changesets)

        if lineChanged || skipLine {
            writeBufferToFile = true
        }

        if !skipLine {
            buffer.WriteString(newLine + "\n")
        }

        line, e = Readln(r)
    }

    // --mode=lineinfile
    if opts.ModeIsLineInFile {
        lifBuffer, lifStatus := handleLineInFile(changesets, buffer)
        if lifStatus {
            buffer.Reset()
            buffer.WriteString(lifBuffer.String())
            writeBufferToFile = lifStatus
        }
    }

    // --output
    // --output-strip-ext
    // enforcing writing of file (creating new file)
    if (opts.Output != "" || opts.OutputStripFileExt != "") {
        writeBufferToFile = true
    }

    if writeBufferToFile {
        output, status = writeContentToFile(fileitem, buffer)
    } else {
        output = fmt.Sprintf("%s no match", fileitem.Path)
    }

    return output, status, err
}

// Apply changesets to file
func applyTemplateToFile(fileitem fileitem, changesets []changeset) (string, bool, error) {
    var (
        err error = nil
        output string = ""
        status bool = true
    )

    // try open file
    buffer, err := ioutil.ReadFile(fileitem.Path)
    if err != nil {
        return output, false, err
    }

    content := parseContentAsTemplate(string(buffer), changesets)

    output, status = writeContentToFile(fileitem, content)

    return output, status, err
}

func applyChangesetsToLine(line string, changesets []changeset) (string, bool, bool) {
    changed := false
    skipLine := false

    for i, changeset := range changesets {
        // --once, only do changeset once if already applied to file
        if opts.Once != "" && changeset.MatchFound {
            // --once=unique, skip matching lines
            if opts.Once == "unique" && searchMatch(line, changeset) {
                // matching line, not writing to buffer as requsted
                skipLine = true
                changed = true
                break
            }
        } else {
            // search and replace
            if searchMatch(line, changeset) {
                // --mode=line or --mode=lineinfile
                if opts.ModeIsReplaceLine || opts.ModeIsLineInFile {
                    if opts.RegexBackref {
                        // get match
                        line = string(changeset.Search.Find([]byte(line)))

                        // replace regex backrefs in match
                        line = changeset.Search.ReplaceAllString(line, changeset.Replace)
                    } else {
                        // replace whole line with replace term
                        line = changeset.Replace
                    }
                } else {
                    // replace only term inside line
                    line = replaceText(line, changeset)
                }

                changesets[i].MatchFound = true
                changed = true
            }
        }
    }

    return line, changed, skipLine
}

// Build search term
// Compiles regexp if regexp is used
func buildSearchTerm(term string) (*regexp.Regexp) {
    var ret *regexp.Regexp
    var regex string

    // --regex
    if opts.Regex {
        // use search term as regex
        regex = term
    } else {
        // use search term as normal string, escape it for regex usage
        regex = regexp.QuoteMeta(term)
    }

    // --ignore-case
    if opts.CaseInsensitive {
        regex = "(?i:" + regex + ")"
    }

    // --verbose
    if opts.Verbose {
        logMessage(fmt.Sprintf("Using regular expression: %s", regex))
    }

    // --regex-posix
    if opts.RegexPosix {
        ret = regexp.MustCompilePOSIX(regex)
    } else {
        ret = regexp.MustCompile(regex)
    }

    return ret
}

// handle special cli options
// eg. --help
//     --version
//     --path
//     --mode=...
func handleSpecialCliOptions(args []string) ([]string) {

    // --dumpversion
    if (opts.ShowOnlyVersion) {
        fmt.Println(Version)
        os.Exit(0)
    }

    // --version
    if (opts.ShowVersion) {
        fmt.Println(fmt.Sprintf("go-replace version %s", Version))
        fmt.Println(fmt.Sprintf("Copyright (C) 2017 %s", Author))
        os.Exit(0)
    }

    // --help
    if (opts.ShowHelp) {
        argparser.WriteHelp(os.Stdout)
        os.Exit(1)
    }

    // --mode
    switch mode := opts.Mode; mode {
        case "replace":
            opts.ModeIsReplaceMatch = true
            opts.ModeIsReplaceLine = false
            opts.ModeIsLineInFile = false
            opts.ModeIsTemplate = false
        case "line":
            opts.ModeIsReplaceMatch = false
            opts.ModeIsReplaceLine = true
            opts.ModeIsLineInFile = false
            opts.ModeIsTemplate = false
        case "lineinfile":
            opts.ModeIsReplaceMatch = false
            opts.ModeIsReplaceLine = false
            opts.ModeIsLineInFile = true
            opts.ModeIsTemplate = false
        case "template":
            opts.ModeIsReplaceMatch = false
            opts.ModeIsReplaceLine = false
            opts.ModeIsLineInFile = false
            opts.ModeIsTemplate = true
    }

    // --path
    if (opts.Path != "") {
        searchFilesInPath(opts.Path, func(f os.FileInfo, path string) {
            args = append(args, path)
        })
    }

    // --output
    if (opts.Output != "" && len(args) > 1) {
        logFatalErrorAndExit(errors.New("Only one file is allowed when using --output"), 1)
    }

    if opts.LineinfileBefore != "" || opts.LineinfileAfter != "" {
        if ! opts.ModeIsLineInFile {
            logFatalErrorAndExit(errors.New("--lineinfile-after and --lineinfile-before only valid in --mode=lineinfile"), 1)
        }

        if opts.LineinfileBefore != "" && opts.LineinfileAfter != "" {
            logFatalErrorAndExit(errors.New("Only --lineinfile-after or --lineinfile-before is allowed in --mode=lineinfile"), 1)
        }
    }

    return args
}

func actionProcessStdinReplace(changesets []changeset) (int) {
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        line := scanner.Text()

        newLine, _, skipLine := applyChangesetsToLine(line, changesets)

        if !skipLine {
            fmt.Println(newLine)
        }
    }

    return 0
}

func actionProcessStdinTemplate(changesets []changeset) (int) {
    var buffer bytes.Buffer

    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        buffer.WriteString(scanner.Text() + "\n")
    }

    content := parseContentAsTemplate(buffer.String(), changesets)
    fmt.Print(content.String())

    return 0
}

func actionProcessFiles(changesets []changeset, fileitems []fileitem) (int) {
    // check if there is at least one file to process
    if (len(fileitems) == 0) {
        if (opts.IgnoreEmpty) {
            // no files found, but we should ignore empty filelist
            logMessage("No files found, requsted to ignore this")
            os.Exit(0)
        } else {
            // no files found, print error and exit with error code
            logFatalErrorAndExit(errors.New("No files specified"), 1)
        }
    }

    results := make(chan changeresult)

    wg := sizedwaitgroup.New(opts.ThreadCount)

    // process file list
    for _, file := range fileitems {
        wg.Add()
        go func(file fileitem, changesets []changeset) {
            var (
                err error = nil
                output string = ""
                status bool = true
            )

            if opts.ModeIsTemplate {
                output, status, err = applyTemplateToFile(file, changesets)
            } else {
                output, status, err = applyChangesetsToFile(file, changesets)
            }
            results <- changeresult{file, output, status, err}
            wg.Done()
        } (file, changesets);
    }

    // wait for all changes to be processed
    go func() {
        wg.Wait()
        close(results)
    }()

    // show results
    errorCount := 0
    for result := range results {
        if result.Error != nil {
            logError(result.Error)
            errorCount++
        } else if opts.Verbose {
            title := fmt.Sprintf("%s:", result.File.Path)

            fmt.Fprintln(os.Stderr, "")
            fmt.Fprintln(os.Stderr, title)
            fmt.Fprintln(os.Stderr, strings.Repeat("-", len(title)))
            fmt.Fprintln(os.Stderr, "")
            fmt.Fprintln(os.Stderr, result.Output)
            fmt.Fprintln(os.Stderr, "")
        }
    }

    if errorCount >= 1 {
        fmt.Fprintln(os.Stderr, fmt.Sprintf("[ERROR] %s failed with %d error(s)", argparser.Command.Name, errorCount))
        return 1
    }

    return 0
}

func buildChangesets() ([]changeset){
    var changesets []changeset

    if !opts.ModeIsTemplate {
        if len(opts.Search) == 0 || len(opts.Replace) == 0 {
            // error: unequal numbers of search and replace options
            logFatalErrorAndExit(errors.New("Missing either --search or --replace for this mode"), 1)
        }
    }

    // check if search and replace options have equal lenght (equal number of options)
    if len(opts.Search) != len(opts.Replace) {
        // error: unequal numbers of search and replace options
        logFatalErrorAndExit(errors.New("Unequal numbers of search or replace options"), 1)
    }

    // build changesets
    for i := range opts.Search {
        search := opts.Search[i]
        replace := opts.Replace[i]

        changeset := changeset{search, buildSearchTerm(search), replace, false}
        changesets = append(changesets, changeset)
    }

    return changesets
}

func buildFileitems(args []string) ([]fileitem) {
    var fileitems []fileitem

    for _, filepath := range args {
        file := fileitem{filepath, filepath}

        if opts.Output != "" {
            // use specific output
            file.Output = opts.Output
        } else if opts.OutputStripFileExt != "" {
            // remove file ext from saving destination
            file.Output = strings.TrimSuffix(file.Output, opts.OutputStripFileExt)
        } else if strings.Contains(filepath, ":") {
            // argument like "source:destination"
            split := strings.SplitN(filepath, ":", 2)

            file.Path = split[0]
            file.Output = split[1]
        }

        fileitems = append(fileitems, file)
    }

    return fileitems
}

var argparser *flags.Parser
func main() {
    argparser = flags.NewParser(&opts, flags.PassDoubleDash)
    args, err := argparser.Parse()

    args = handleSpecialCliOptions(args)

    // check if there is an parse error
    if err != nil {
        logFatalErrorAndExit(err, 1)
    }

    changesets := buildChangesets()
    fileitems := buildFileitems(args)

    exitMode := 0
    if opts.Stdin {
        if opts.ModeIsTemplate {
            // use stdin as input
            exitMode = actionProcessStdinTemplate(changesets)
        } else {
            // use stdin as input
            exitMode = actionProcessStdinReplace(changesets)
        }
    } else {
        // use and process files (see args)
        exitMode = actionProcessFiles(changesets, fileitems)
    }

    os.Exit(exitMode)
}
<commit_msg>Raise threads to 20 by default<commit_after>package main

import (
    "fmt"
    "errors"
    "bytes"
    "io/ioutil"
    "bufio"
    "os"
    "strings"
    "regexp"
    flags "github.com/jessevdk/go-flags"
    "github.com/remeh/sizedwaitgroup"
)

const (
    Author  = "webdevops.io"
    Version = "1.1.1"
)

type changeset struct {
    SearchPlain string
    Search      *regexp.Regexp
    Replace     string
    MatchFound  bool
}

type changeresult struct {
    File   fileitem
    Output string
    Status bool
    Error  error
}

type fileitem struct {
    Path        string
    Output      string
}

var opts struct {
    ThreadCount             int      `           long:"threads"                       description:"Set thread concurrency for replacing in multiple files at same time" default:"20"`
    Mode                    string   `short:"m"  long:"mode"                          description:"replacement mode - replace: replace match with term; line: replace line with term; lineinfile: replace line with term or if not found append to term to file; template: parse content as golang template, search value have to start uppercase" default:"replace" choice:"replace" choice:"line" choice:"lineinfile" choice:"template"`
    ModeIsReplaceMatch      bool
    ModeIsReplaceLine       bool
    ModeIsLineInFile        bool
    ModeIsTemplate          bool
    Search                  []string `short:"s"  long:"search"                        description:"search term"`
    Replace                 []string `short:"r"  long:"replace"                       description:"replacement term"`
    LineinfileBefore        string   `           long:"lineinfile-before"             description:"add line before this regex"`
    LineinfileAfter         string   `           long:"lineinfile-after"              description:"add line after this regex"`
    CaseInsensitive         bool     `short:"i"  long:"case-insensitive"              description:"ignore case of pattern to match upper and lowercase characters"`
    Stdin                   bool     `           long:"stdin"                         description:"process stdin as input"`
    Output                  string   `short:"o"  long:"output"                        description:"write changes to this file (in one file mode)"`
    OutputStripFileExt      string   `           long:"output-strip-ext"              description:"strip file extension from written files (also available in multi file mode)"`
    Once                    string   `           long:"once"                          description:"replace search term only one in a file, keep duplicaes (keep, default) or remove them (unique)" optional:"true" optional-value:"keep" choice:"keep" choice:"unique"`
    Regex                   bool     `           long:"regex"                         description:"treat pattern as regex"`
    RegexBackref            bool     `           long:"regex-backrefs"                description:"enable backreferences in replace term"`
    RegexPosix              bool     `           long:"regex-posix"                   description:"parse regex term as POSIX regex"`
    Path                    string   `           long:"path"                          description:"use files in this path"`
    PathPattern             string   `           long:"path-pattern"                  description:"file pattern (* for wildcard, only basename of file)"`
    PathRegex               string   `           long:"path-regex"                    description:"file pattern (regex, full path)"`
    IgnoreEmpty             bool     `           long:"ignore-empty"                  description:"ignore empty file list, otherwise this will result in an error"`
    Verbose                 bool     `short:"v"  long:"verbose"                       description:"verbose mode"`
    DryRun                  bool     `           long:"dry-run"                       description:"dry run mode"`
    ShowVersion             bool     `short:"V"  long:"version"                       description:"show version and exit"`
    ShowOnlyVersion         bool     `           long:"dumpversion"                   description:"show only version number and exit"`
    ShowHelp                bool     `short:"h"  long:"help"                          description:"show this help message"`
}

var pathFilterDirectories = []string{"autom4te.cache", "blib", "_build", ".bzr", ".cdv", "cover_db", "CVS", "_darcs", "~.dep", "~.dot", ".git", ".hg", "~.nib", ".pc", "~.plst", "RCS", "SCCS", "_sgbak", ".svn", "_obj", ".idea"}

// Apply changesets to file
func applyChangesetsToFile(fileitem fileitem, changesets []changeset) (string, bool, error) {
    var (
        err error = nil
        output string = ""
        status bool = true
    )

    // try open file
    file, err := os.Open(fileitem.Path)
    if err != nil {
        return output, false, err
    }

    writeBufferToFile := false
    var buffer bytes.Buffer

    r := bufio.NewReader(file)
    line, e := Readln(r)
    for e == nil {
        newLine, lineChanged, skipLine := applyChangesetsToLine(line, changesets)

        if lineChanged || skipLine {
            writeBufferToFile = true
        }

        if !skipLine {
            buffer.WriteString(newLine + "\n")
        }

        line, e = Readln(r)
    }

    // --mode=lineinfile
    if opts.ModeIsLineInFile {
        lifBuffer, lifStatus := handleLineInFile(changesets, buffer)
        if lifStatus {
            buffer.Reset()
            buffer.WriteString(lifBuffer.String())
            writeBufferToFile = lifStatus
        }
    }

    // --output
    // --output-strip-ext
    // enforcing writing of file (creating new file)
    if (opts.Output != "" || opts.OutputStripFileExt != "") {
        writeBufferToFile = true
    }

    if writeBufferToFile {
        output, status = writeContentToFile(fileitem, buffer)
    } else {
        output = fmt.Sprintf("%s no match", fileitem.Path)
    }

    return output, status, err
}

// Apply changesets to file
func applyTemplateToFile(fileitem fileitem, changesets []changeset) (string, bool, error) {
    var (
        err error = nil
        output string = ""
        status bool = true
    )

    // try open file
    buffer, err := ioutil.ReadFile(fileitem.Path)
    if err != nil {
        return output, false, err
    }

    content := parseContentAsTemplate(string(buffer), changesets)

    output, status = writeContentToFile(fileitem, content)

    return output, status, err
}

func applyChangesetsToLine(line string, changesets []changeset) (string, bool, bool) {
    changed := false
    skipLine := false

    for i, changeset := range changesets {
        // --once, only do changeset once if already applied to file
        if opts.Once != "" && changeset.MatchFound {
            // --once=unique, skip matching lines
            if opts.Once == "unique" && searchMatch(line, changeset) {
                // matching line, not writing to buffer as requsted
                skipLine = true
                changed = true
                break
            }
        } else {
            // search and replace
            if searchMatch(line, changeset) {
                // --mode=line or --mode=lineinfile
                if opts.ModeIsReplaceLine || opts.ModeIsLineInFile {
                    if opts.RegexBackref {
                        // get match
                        line = string(changeset.Search.Find([]byte(line)))

                        // replace regex backrefs in match
                        line = changeset.Search.ReplaceAllString(line, changeset.Replace)
                    } else {
                        // replace whole line with replace term
                        line = changeset.Replace
                    }
                } else {
                    // replace only term inside line
                    line = replaceText(line, changeset)
                }

                changesets[i].MatchFound = true
                changed = true
            }
        }
    }

    return line, changed, skipLine
}

// Build search term
// Compiles regexp if regexp is used
func buildSearchTerm(term string) (*regexp.Regexp) {
    var ret *regexp.Regexp
    var regex string

    // --regex
    if opts.Regex {
        // use search term as regex
        regex = term
    } else {
        // use search term as normal string, escape it for regex usage
        regex = regexp.QuoteMeta(term)
    }

    // --ignore-case
    if opts.CaseInsensitive {
        regex = "(?i:" + regex + ")"
    }

    // --verbose
    if opts.Verbose {
        logMessage(fmt.Sprintf("Using regular expression: %s", regex))
    }

    // --regex-posix
    if opts.RegexPosix {
        ret = regexp.MustCompilePOSIX(regex)
    } else {
        ret = regexp.MustCompile(regex)
    }

    return ret
}

// handle special cli options
// eg. --help
//     --version
//     --path
//     --mode=...
func handleSpecialCliOptions(args []string) ([]string) {

    // --dumpversion
    if (opts.ShowOnlyVersion) {
        fmt.Println(Version)
        os.Exit(0)
    }

    // --version
    if (opts.ShowVersion) {
        fmt.Println(fmt.Sprintf("go-replace version %s", Version))
        fmt.Println(fmt.Sprintf("Copyright (C) 2017 %s", Author))
        os.Exit(0)
    }

    // --help
    if (opts.ShowHelp) {
        argparser.WriteHelp(os.Stdout)
        os.Exit(1)
    }

    // --mode
    switch mode := opts.Mode; mode {
        case "replace":
            opts.ModeIsReplaceMatch = true
            opts.ModeIsReplaceLine = false
            opts.ModeIsLineInFile = false
            opts.ModeIsTemplate = false
        case "line":
            opts.ModeIsReplaceMatch = false
            opts.ModeIsReplaceLine = true
            opts.ModeIsLineInFile = false
            opts.ModeIsTemplate = false
        case "lineinfile":
            opts.ModeIsReplaceMatch = false
            opts.ModeIsReplaceLine = false
            opts.ModeIsLineInFile = true
            opts.ModeIsTemplate = false
        case "template":
            opts.ModeIsReplaceMatch = false
            opts.ModeIsReplaceLine = false
            opts.ModeIsLineInFile = false
            opts.ModeIsTemplate = true
    }

    // --path
    if (opts.Path != "") {
        searchFilesInPath(opts.Path, func(f os.FileInfo, path string) {
            args = append(args, path)
        })
    }

    // --output
    if (opts.Output != "" && len(args) > 1) {
        logFatalErrorAndExit(errors.New("Only one file is allowed when using --output"), 1)
    }

    if opts.LineinfileBefore != "" || opts.LineinfileAfter != "" {
        if ! opts.ModeIsLineInFile {
            logFatalErrorAndExit(errors.New("--lineinfile-after and --lineinfile-before only valid in --mode=lineinfile"), 1)
        }

        if opts.LineinfileBefore != "" && opts.LineinfileAfter != "" {
            logFatalErrorAndExit(errors.New("Only --lineinfile-after or --lineinfile-before is allowed in --mode=lineinfile"), 1)
        }
    }

    return args
}

func actionProcessStdinReplace(changesets []changeset) (int) {
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        line := scanner.Text()

        newLine, _, skipLine := applyChangesetsToLine(line, changesets)

        if !skipLine {
            fmt.Println(newLine)
        }
    }

    return 0
}

func actionProcessStdinTemplate(changesets []changeset) (int) {
    var buffer bytes.Buffer

    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        buffer.WriteString(scanner.Text() + "\n")
    }

    content := parseContentAsTemplate(buffer.String(), changesets)
    fmt.Print(content.String())

    return 0
}

func actionProcessFiles(changesets []changeset, fileitems []fileitem) (int) {
    // check if there is at least one file to process
    if (len(fileitems) == 0) {
        if (opts.IgnoreEmpty) {
            // no files found, but we should ignore empty filelist
            logMessage("No files found, requsted to ignore this")
            os.Exit(0)
        } else {
            // no files found, print error and exit with error code
            logFatalErrorAndExit(errors.New("No files specified"), 1)
        }
    }

    results := make(chan changeresult)

    wg := sizedwaitgroup.New(opts.ThreadCount)

    // process file list
    for _, file := range fileitems {
        wg.Add()
        go func(file fileitem, changesets []changeset) {
            var (
                err error = nil
                output string = ""
                status bool = true
            )

            if opts.ModeIsTemplate {
                output, status, err = applyTemplateToFile(file, changesets)
            } else {
                output, status, err = applyChangesetsToFile(file, changesets)
            }
            results <- changeresult{file, output, status, err}
            wg.Done()
        } (file, changesets);
    }

    // wait for all changes to be processed
    go func() {
        wg.Wait()
        close(results)
    }()

    // show results
    errorCount := 0
    for result := range results {
        if result.Error != nil {
            logError(result.Error)
            errorCount++
        } else if opts.Verbose {
            title := fmt.Sprintf("%s:", result.File.Path)

            fmt.Fprintln(os.Stderr, "")
            fmt.Fprintln(os.Stderr, title)
            fmt.Fprintln(os.Stderr, strings.Repeat("-", len(title)))
            fmt.Fprintln(os.Stderr, "")
            fmt.Fprintln(os.Stderr, result.Output)
            fmt.Fprintln(os.Stderr, "")
        }
    }

    if errorCount >= 1 {
        fmt.Fprintln(os.Stderr, fmt.Sprintf("[ERROR] %s failed with %d error(s)", argparser.Command.Name, errorCount))
        return 1
    }

    return 0
}

func buildChangesets() ([]changeset){
    var changesets []changeset

    if !opts.ModeIsTemplate {
        if len(opts.Search) == 0 || len(opts.Replace) == 0 {
            // error: unequal numbers of search and replace options
            logFatalErrorAndExit(errors.New("Missing either --search or --replace for this mode"), 1)
        }
    }

    // check if search and replace options have equal lenght (equal number of options)
    if len(opts.Search) != len(opts.Replace) {
        // error: unequal numbers of search and replace options
        logFatalErrorAndExit(errors.New("Unequal numbers of search or replace options"), 1)
    }

    // build changesets
    for i := range opts.Search {
        search := opts.Search[i]
        replace := opts.Replace[i]

        changeset := changeset{search, buildSearchTerm(search), replace, false}
        changesets = append(changesets, changeset)
    }

    return changesets
}

func buildFileitems(args []string) ([]fileitem) {
    var fileitems []fileitem

    for _, filepath := range args {
        file := fileitem{filepath, filepath}

        if opts.Output != "" {
            // use specific output
            file.Output = opts.Output
        } else if opts.OutputStripFileExt != "" {
            // remove file ext from saving destination
            file.Output = strings.TrimSuffix(file.Output, opts.OutputStripFileExt)
        } else if strings.Contains(filepath, ":") {
            // argument like "source:destination"
            split := strings.SplitN(filepath, ":", 2)

            file.Path = split[0]
            file.Output = split[1]
        }

        fileitems = append(fileitems, file)
    }

    return fileitems
}

var argparser *flags.Parser
func main() {
    argparser = flags.NewParser(&opts, flags.PassDoubleDash)
    args, err := argparser.Parse()

    args = handleSpecialCliOptions(args)

    // check if there is an parse error
    if err != nil {
        logFatalErrorAndExit(err, 1)
    }

    changesets := buildChangesets()
    fileitems := buildFileitems(args)

    exitMode := 0
    if opts.Stdin {
        if opts.ModeIsTemplate {
            // use stdin as input
            exitMode = actionProcessStdinTemplate(changesets)
        } else {
            // use stdin as input
            exitMode = actionProcessStdinReplace(changesets)
        }
    } else {
        // use and process files (see args)
        exitMode = actionProcessFiles(changesets, fileitems)
    }

    os.Exit(exitMode)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/binary"
	"fmt"
	"log"
	"time"

	"golang.org/x/mobile/app"
	"golang.org/x/mobile/audio"
	"golang.org/x/mobile/event"
	"golang.org/x/mobile/f32"
	"golang.org/x/mobile/geom"
	"golang.org/x/mobile/gl"
	"golang.org/x/mobile/gl/glutil"
)

const (
	numBeats  = 8
	numTracks = 8
)

var (
	program  gl.Program
	position gl.Attrib
	offset   gl.Uniform
	color    gl.Uniform
	buf      gl.Buffer

	index    int
	green    float32
	greenDec bool
	touchLoc geom.Point

	started bool
)

func main() {
	app.Run(app.Callbacks{
		Start: start,
		Stop:  stop,
		Draw:  draw,
		Touch: touch,
	})
}

func start() {
	var err error
	program, err = glutil.CreateProgram(vertexShader, fragmentShader)
	if err != nil {
		log.Printf("error creating GL program: %v", err)
		return
	}

	buf = gl.CreateBuffer()
	gl.BindBuffer(gl.ARRAY_BUFFER, buf)
	gl.BufferData(gl.ARRAY_BUFFER, rectData, gl.STATIC_DRAW)

	position = gl.GetAttribLocation(program, "position")
	color = gl.GetUniformLocation(program, "color")
	offset = gl.GetUniformLocation(program, "offset")

	if started {
		return
	}
	started = true

	for i := 0; i < numTracks; i++ {
		rc, err := app.Open(fmt.Sprintf("track%d.wav", i))
		if err != nil {
			log.Fatal(err)
		}

		p, err := audio.NewPlayer(rc, audio.Stereo16, 44100)
		if err != nil {
			log.Fatal(err)
		}
		players[i] = p
	}

	// hi hat
	hits[0][1] = true
	hits[2][1] = true
	hits[4][1] = true
	hits[6][1] = true

	// kick
	hits[5][2] = true
	hits[7][2] = true

	// bass
	hits[0][4] = true
	hits[3][4] = true
	hits[5][4] = true
	hits[6][4] = true

	// bass2
	hits[2][6] = true

	hits[4][7] = true

	go func() {
		for {
			index = (index + 1) % numBeats
			for t := 0; t < numTracks; t++ {
				go func(t int) {
					if hits[index][t] {
						players[t].Play()
					}
				}(t)
			}
			time.Sleep(400 * time.Millisecond)
		}
	}()
}

func stop() {
	for _, p := range players {
		p.Stop()
	}
	gl.DeleteProgram(program)
	gl.DeleteBuffer(buf)
}

func touch(t event.Touch) {
	touchLoc = t.Loc
}

var rectData = f32.Bytes(binary.LittleEndian,
	0, 0,
	0, 0.1,
	0.1, 0,
	0.1, 0.1,
)

var (
	hits    [numBeats][numTracks]bool
	players [numTracks]*audio.Player
)

func draw() {
	gl.ClearColor(0, 0, 0, 1)
	gl.Clear(gl.COLOR_BUFFER_BIT)
	gl.UseProgram(program)

	if greenDec {
		green -= 0.01
	} else {
		green += 0.01
	}
	if green <= 0.2 {
		greenDec = false
	}
	if green >= 0.5 {
		greenDec = true
	}

	for i := 0; i < numBeats; i++ {
		for j := 0; j < numTracks; j++ {
			var c float32
			switch {
			case hits[i][j]:
				c = 1
			case i == index:
				c = green
			default:
				c = 0
			}
			drawButton(c, float32(i)*1/numBeats, float32(j)*1/numTracks)
		}
	}
}

func drawButton(g, x, y float32) {
	gl.Uniform4f(color, 0.1, g, 0.4, 1) // color
	gl.Uniform2f(offset, x, y)          // position

	gl.BindBuffer(gl.ARRAY_BUFFER, buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0)
	gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
	gl.DisableVertexAttribArray(position)
}

const vertexShader = `#version 100
uniform vec2 offset;
attribute vec4 position;
void main() {
  // offset comes in with x/y values between 0 and 1.
  // position bounds are -1 to 1.
  vec4 offset4 = vec4(2.0*offset.x-1.0, 1.0-2.0*offset.y, 0, 0);
  gl_Position = position + offset4;
}`

const fragmentShader = `#version 100
precision mediump float;
uniform vec4 color;
void main() {
  gl_FragColor = color;
}`
<commit_msg>add a TODO about the touch handler.<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/binary"
	"fmt"
	"log"
	"time"

	"golang.org/x/mobile/app"
	"golang.org/x/mobile/audio"
	"golang.org/x/mobile/event"
	"golang.org/x/mobile/f32"
	"golang.org/x/mobile/geom"
	"golang.org/x/mobile/gl"
	"golang.org/x/mobile/gl/glutil"
)

const (
	numBeats  = 8
	numTracks = 8
)

var (
	program  gl.Program
	position gl.Attrib
	offset   gl.Uniform
	color    gl.Uniform
	buf      gl.Buffer

	index    int
	green    float32
	greenDec bool
	touchLoc geom.Point

	started bool
)

func main() {
	app.Run(app.Callbacks{
		Start: start,
		Stop:  stop,
		Draw:  draw,
		Touch: touch,
	})
}

func start() {
	var err error
	program, err = glutil.CreateProgram(vertexShader, fragmentShader)
	if err != nil {
		log.Printf("error creating GL program: %v", err)
		return
	}

	buf = gl.CreateBuffer()
	gl.BindBuffer(gl.ARRAY_BUFFER, buf)
	gl.BufferData(gl.ARRAY_BUFFER, rectData, gl.STATIC_DRAW)

	position = gl.GetAttribLocation(program, "position")
	color = gl.GetUniformLocation(program, "color")
	offset = gl.GetUniformLocation(program, "offset")

	if started {
		return
	}
	started = true

	for i := 0; i < numTracks; i++ {
		rc, err := app.Open(fmt.Sprintf("track%d.wav", i))
		if err != nil {
			log.Fatal(err)
		}

		p, err := audio.NewPlayer(rc, audio.Stereo16, 44100)
		if err != nil {
			log.Fatal(err)
		}
		players[i] = p
	}

	// hi hat
	hits[0][1] = true
	hits[2][1] = true
	hits[4][1] = true
	hits[6][1] = true

	// kick
	hits[5][2] = true
	hits[7][2] = true

	// bass
	hits[0][4] = true
	hits[3][4] = true
	hits[5][4] = true
	hits[6][4] = true

	// bass2
	hits[2][6] = true

	hits[4][7] = true

	go func() {
		for {
			index = (index + 1) % numBeats
			for t := 0; t < numTracks; t++ {
				go func(t int) {
					if hits[index][t] {
						players[t].Play()
					}
				}(t)
			}
			time.Sleep(400 * time.Millisecond)
		}
	}()
}

func stop() {
	for _, p := range players {
		p.Stop()
	}
	gl.DeleteProgram(program)
	gl.DeleteBuffer(buf)
}

func touch(t event.Touch) {
	// TODO(jbd): Turn on/off the hits based on the touch location.
	touchLoc = t.Loc
}

var rectData = f32.Bytes(binary.LittleEndian,
	0, 0,
	0, 0.1,
	0.1, 0,
	0.1, 0.1,
)

var (
	hits    [numBeats][numTracks]bool
	players [numTracks]*audio.Player
)

func draw() {
	gl.ClearColor(0, 0, 0, 1)
	gl.Clear(gl.COLOR_BUFFER_BIT)
	gl.UseProgram(program)

	if greenDec {
		green -= 0.01
	} else {
		green += 0.01
	}
	if green <= 0.2 {
		greenDec = false
	}
	if green >= 0.5 {
		greenDec = true
	}

	for i := 0; i < numBeats; i++ {
		for j := 0; j < numTracks; j++ {
			var c float32
			switch {
			case hits[i][j]:
				c = 1
			case i == index:
				c = green
			default:
				c = 0
			}
			drawButton(c, float32(i)*1/numBeats, float32(j)*1/numTracks)
		}
	}
}

func drawButton(g, x, y float32) {
	gl.Uniform4f(color, 0.1, g, 0.4, 1) // color
	gl.Uniform2f(offset, x, y)          // position

	gl.BindBuffer(gl.ARRAY_BUFFER, buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0)
	gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
	gl.DisableVertexAttribArray(position)
}

const vertexShader = `#version 100
uniform vec2 offset;
attribute vec4 position;
void main() {
  // offset comes in with x/y values between 0 and 1.
  // position bounds are -1 to 1.
  vec4 offset4 = vec4(2.0*offset.x-1.0, 1.0-2.0*offset.y, 0, 0);
  gl_Position = position + offset4;
}`

const fragmentShader = `#version 100
precision mediump float;
uniform vec4 color;
void main() {
  gl_FragColor = color;
}`
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	cb "github.com/clearblade/Go-SDK"
	"github.com/clearblade/cblib"
	"os"
	"strings"
)

func init() {
	flag.StringVar(&cblib.URL, "url", "", "Platform URL")
	flag.BoolVar(&cblib.Help, "help", false, "Print help message")
	flag.StringVar(&cblib.Email, "email", "", "Developer username")
	flag.StringVar(&cblib.Password, "password", "", "Developer password")
}

func main() {
	if err := setupFromOptsAndCBMeta(); err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	client, err := doAuth()
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	cmd := strings.ToLower(flag.Arg(0))
	subCommand, err := cblib.GetCommand(cmd)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	subArgs := flag.Args()
	err = subCommand.Execute(client, subArgs[1:])
	if err != nil {
		fmt.Printf("Aborting: %s\n", err.Error())
	}
}

func setupFromOptsAndCBMeta() error {
	flag.Parse()
	//cblib.DevToken, _ = cblib.Load_auth_info(cblib.AuthInfoFile)
	err := cblib.GoToRepoRootDir()
	if err != nil && err.Error() != cblib.SpecialNoCBMetaError {
		return err
	}
	if cblib.URL != "" {
		cb.CB_ADDR = cblib.URL
	} else if url, ok := cblib.MetaInfo["platformURL"]; ok {
		cblib.URL = url.(string)
		cb.CB_ADDR = url.(string)
	} else {
		cblib.URL = cb.CB_ADDR
	}

	if cblib.DevToken != "" {
		if tok, ok := cblib.MetaInfo["token"]; ok {
			cblib.DevToken = tok.(string)
		}
	}

	fmt.Printf("Using system at '%s'\n", cb.CB_ADDR)

	if cblib.Email == "" {
		if email, ok := cblib.MetaInfo["developerEmail"]; ok {
			cblib.Email = email.(string)
		}
	} else {
		cblib.CommandLineEmail = true
	}
	return nil
}

func doAuth() (*cb.DevClient, error) {
	if cblib.Email != "" && cblib.Password != "" {
		return cblib.AuthUserPass(cblib.Email, cblib.Password)
	} else if cblib.CommandLineEmail {
		return cblib.AuthPromptPass(cblib.Email)
	} else {
		return cblib.Auth("")
	}
}
<commit_msg>death to .cbauth<commit_after>package main

import (
	"flag"
	"fmt"
	cb "github.com/clearblade/Go-SDK"
	"github.com/clearblade/cblib"
	"os"
	"strings"
)

func init() {
	flag.StringVar(&cblib.URL, "url", "", "Platform URL")
	flag.BoolVar(&cblib.Help, "help", false, "Print help message")
	flag.StringVar(&cblib.Email, "email", "", "Developer username")
	flag.StringVar(&cblib.Password, "password", "", "Developer password")
}

func main() {
	if err := setupFromOptsAndCBMeta(); err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	client, err := doAuth()
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	cmd := strings.ToLower(flag.Arg(0))
	subCommand, err := cblib.GetCommand(cmd)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	subArgs := flag.Args()
	err = subCommand.Execute(client, subArgs[1:])
	if err != nil {
		fmt.Printf("Aborting: %s\n", err.Error())
	}
}

func setupFromOptsAndCBMeta() error {
	flag.Parse()
	//cblib.DevToken, _ = cblib.Load_auth_info(cblib.AuthInfoFile)
	err := cblib.GoToRepoRootDir()
	if err != nil && err.Error() != cblib.SpecialNoCBMetaError {
		return err
	}
	if cblib.URL != "" {
		cb.CB_ADDR = cblib.URL
	} else if url, ok := cblib.MetaInfo["platformURL"]; ok {
		cblib.URL = url.(string)
		cb.CB_ADDR = url.(string)
	} else {
		cblib.URL = cb.CB_ADDR
	}

	if cblib.DevToken == "" {
		if tok, ok := cblib.MetaInfo["token"]; ok {
			cblib.DevToken = tok.(string)
		}
	}

	fmt.Printf("Using system at '%s'\n", cb.CB_ADDR)

	if cblib.Email == "" {
		if email, ok := cblib.MetaInfo["developerEmail"]; ok {
			cblib.Email = email.(string)
		}
	} else {
		cblib.CommandLineEmail = true
	}
	return nil
}

func doAuth() (*cb.DevClient, error) {
	if cblib.Email != "" && cblib.Password != "" {
		return cblib.AuthUserPass(cblib.Email, cblib.Password)
	} else if cblib.DevToken != "" {
		return cblib.Auth(cblib.DevToken)
	} else if cblib.CommandLineEmail {
		return cblib.AuthPromptPass(cblib.Email)
	} else {
		return cblib.Auth("")
	}
}
<|endoftext|>