<commit_before>package lessgo

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

	"github.com/lessgo/lessgo/config"
	"github.com/lessgo/lessgo/logs"
	"github.com/lessgo/lessgo/session"
)

type (
	// Config is the main struct for BConfig
	Config struct {
		AppName             string // Application name
		Info                Info   // Application info
		Debug               bool   // enable/disable debug mode.
		CrossDomain         bool
		RouterCaseSensitive bool  // 是否路由忽略大小写匹配，默认是 true，区分大小写
		MaxMemoryMB         int64 // 文件上传默认内存缓存大小，单位MB
		Listen              Listen
		Session             SessionConfig
		Log                 LogConfig
		FileCache           FileCacheConfig
		DefaultDB           string
		DBList              map[string]DBConfig
	}
	Info struct {
		Version           string
		Description       string
		Email             string
		TermsOfServiceUrl string
		License           string
		LicenseUrl        string
	}
	// Listen holds for http and https related config
	Listen struct {
		Graceful      bool // Graceful means use graceful module to start the server
		Address       string
		ReadTimeout   int64
		WriteTimeout  int64
		EnableHTTPS   bool
		HTTPSKeyFile  string
		HTTPSCertFile string
	}
	// SessionConfig holds session related config
	SessionConfig struct {
		SessionOn               bool
		SessionProvider         string
		SessionName             string
		SessionGCMaxLifetime    int64
		SessionProviderConfig   string
		SessionCookieLifeTime   int
		SessionAutoSetCookie    bool
		SessionDomain           string
		EnableSidInHttpHeader   bool //	enable store/get the sessionId into/from http headers
		SessionNameInHttpHeader string
		EnableSidInUrlQuery     bool //	enable get the sessionId from Url Query params
	}

	// LogConfig holds Log related config
	LogConfig struct {
		Level     int
		AsyncChan int64
	}
	FileCacheConfig struct {
		CacheSecond       int64 // 静态资源缓存监测频率与缓存动态释放的最大时长，单位秒，默认600秒
		SingleFileAllowMB int64 // 允许的最大文件，单位MB
		MaxCapMB          int64 // 最大缓存总量，单位MB
	}
	// DataBase connection Config
	DBConfig struct {
		Name         string
		Driver       string // Driver：mssql | odbc(mssql) | mysql | mymysql | postgres | sqlite3 | oci8 | goracle
		ConnString   string
		MaxOpenConns int
		MaxIdleConns int
		TableFix     string // 表命名空间是前缀还是后缀：prefix | suffix
		TableSpace   string // 表命名空间
		TableSnake   bool   // 表名使用snake风格或保持不变
		ColumnFix    string // 列命名空间是前缀还是后缀：prefix | suffix
		ColumnSpace  string // 列命名空间
		ColumnSnake  bool   // 列名使用snake风格或保持不变
		DisableCache bool
		ShowExecTime bool
		ShowSql      bool
	}
)

// 项目固定目录文件名称
const (
	BUSINESS_API_DIR  = "BusinessApi"
	BUSINESS_VIEW_DIR = "BusinessView"
	SYSTEM_API_DIR    = "SystemApi"
	SYSTEM_VIEW_DIR   = "SystemView"
	STATIC_DIR        = "Static"
	IMG_DIR           = STATIC_DIR + "/Img"
	JS_DIR            = STATIC_DIR + "/Js"
	CSS_DIR           = STATIC_DIR + "/Css"
	TPL_DIR           = STATIC_DIR + "/Tpl"
	PLUGIN_DIR        = STATIC_DIR + "/Plugin"
	UPLOADS_DIR       = "Uploads"
	COMMON_DIR        = "Common"
	MIDDLEWARE_DIR    = COMMON_DIR + "/Middleware"

	TPL_EXT         = ".tpl"
	STATIC_HTML_EXT = ".html"

	CONFIG_DIR     = "Config"
	APPCONFIG_FILE = CONFIG_DIR + "/app.config"
	DBCONFIG_FILE  = CONFIG_DIR + "/db.config"

	DB_DIR            = COMMON_DIR + "/DB"
	DEFAULTDB_SECTION = "defaultdb"

	VIEW_PKG      = "/View"
	MODULE_SUFFIX = "Module"
)

var (
	// AppConfig is the instance of Config, store the config information from file
	AppConfig = initConfig()
	// GlobalSessions is the instance for the session manager
	GlobalSessions *session.Manager
)

func initConfig() *Config {
	return &Config{
		AppName: "lessgo",
		Info: Info{
			Version:     "0.4.0",
			Description: "A simple, stable, efficient and flexible web framework.",
			// Host:              "127.0.0.1:8080",
			Email:             "henrylee_cn@foxmail.com",
			TermsOfServiceUrl: "https://github.com/lessgo/lessgo",
			License:           "MIT",
			LicenseUrl:        "https://github.com/lessgo/lessgo/raw/master/doc/LICENSE",
		},
		Debug:               true,
		CrossDomain:         false,
		RouterCaseSensitive: false,
		MaxMemoryMB:         64, // 64MB
		Listen: Listen{
			Graceful:      false,
			Address:       "0.0.0.0:8080",
			ReadTimeout:   0,
			WriteTimeout:  0,
			EnableHTTPS:   false,
			HTTPSCertFile: "",
			HTTPSKeyFile:  "",
		},
		Session: SessionConfig{
			SessionOn:               false,
			SessionProvider:         "memory",
			SessionName:             "lessgosessionID",
			SessionGCMaxLifetime:    3600,
			SessionProviderConfig:   "",
			SessionCookieLifeTime:   0, //set cookie default is the browser life
			SessionAutoSetCookie:    true,
			SessionDomain:           "",
			EnableSidInHttpHeader:   false, //	enable store/get the sessionId into/from http headers
			SessionNameInHttpHeader: "Lessgosessionid",
			EnableSidInUrlQuery:     false, //	enable get the sessionId from Url Query params
		},

		FileCache: FileCacheConfig{
			CacheSecond:       600, // 600s
			SingleFileAllowMB: 64,  // 64MB
			MaxCapMB:          256, // 256MB
		},
		Log: LogConfig{
			Level:     logs.DEBUG,
			AsyncChan: 1000,
		},
		DefaultDB: "lessgo",
		DBList: map[string]DBConfig{
			"lessgo": {
				Name:         "lessgo",
				Driver:       "sqlite3",
				ConnString:   DB_DIR + "/sqlite.db",
				MaxOpenConns: 1,
				MaxIdleConns: 1,
				TableFix:     "prefix",
				TableSpace:   "",
				TableSnake:   true,
				ColumnFix:    "prefix",
				ColumnSpace:  "",
				ColumnSnake:  true,
				DisableCache: false,
				ShowExecTime: false,
				ShowSql:      false,
			},
		},
	}
}

func LoadAppConfig() (err error) {
	fname := APPCONFIG_FILE
	iniconf, err := config.NewConfig("ini", fname)
	if err == nil {
		syncSingleConfig("system", AppConfig, iniconf)
		syncSingleConfig("filecache", &AppConfig.FileCache, iniconf)
		syncSingleConfig("info", &AppConfig.Info, iniconf)
		syncSingleConfig("listen", &AppConfig.Listen, iniconf)
		syncSingleConfig("log", &AppConfig.Log, iniconf)
		syncSingleConfig("session", &AppConfig.Session, iniconf)
	} else {
		os.MkdirAll(filepath.Dir(fname), 0777)
		f, err := os.Create(fname)
		if err != nil {
			return err
		}
		f.Close()
		iniconf, err = config.NewConfig("ini", fname)
		if err != nil {
			return err
		}
		initSingleConfig("system", AppConfig, iniconf)
		initSingleConfig("filecache", &AppConfig.FileCache, iniconf)
		initSingleConfig("info", &AppConfig.Info, iniconf)
		initSingleConfig("listen", &AppConfig.Listen, iniconf)
		initSingleConfig("log", &AppConfig.Log, iniconf)
		initSingleConfig("session", &AppConfig.Session, iniconf)
	}

	return iniconf.SaveConfigFile(fname)
}

func LoadDBConfig() (err error) {
	fname := DBCONFIG_FILE
	iniconf, err := config.NewConfig("ini", fname)
	if err == nil {
		sysDB := AppConfig.DBList["lessgo"]
		defDB := sysDB
		delete(AppConfig.DBList, "lessgo") // 移除预设数据库
		for _, section := range iniconf.(*config.IniConfigContainer).Sections() {
			dbconfig := defDB
			syncSingleConfig(section, &dbconfig, iniconf)
			if strings.ToLower(section) == DEFAULTDB_SECTION {
				AppConfig.DefaultDB = dbconfig.Name
			}
			AppConfig.DBList[dbconfig.Name] = dbconfig
		}
		if _, ok := AppConfig.DBList["lessgo"]; !ok {
			section := "lessgo"
			if AppConfig.DefaultDB == "lessgo" {
				section = DEFAULTDB_SECTION
			}
			initSingleConfig(section, &sysDB, iniconf)
		}
	} else {
		os.MkdirAll(filepath.Dir(fname), 0777)
		f, err := os.Create(fname)
		if err != nil {
			return err
		}
		f.Close()
		iniconf, err = config.NewConfig("ini", fname)
		if err != nil {
			return err
		}
		for _, dbconfig := range AppConfig.DBList {
			if AppConfig.DefaultDB == dbconfig.Name {
				initSingleConfig(DEFAULTDB_SECTION, &dbconfig, iniconf)
			} else {
				initSingleConfig(dbconfig.Name, &dbconfig, iniconf)
			}
		}
	}

	return iniconf.SaveConfigFile(fname)
}

func syncSingleConfig(section string, p interface{}, iniconf config.Configer) {
	pt := reflect.TypeOf(p)
	if pt.Kind() != reflect.Ptr {
		return
	}
	pt = pt.Elem()
	if pt.Kind() != reflect.Struct {
		return
	}
	pv := reflect.ValueOf(p).Elem()

	for i := 0; i < pt.NumField(); i++ {
		pf := pv.Field(i)
		if !pf.CanSet() {
			continue
		}
		name := pt.Field(i).Name
		fullname := getfullname(section, name)
		switch pf.Kind() {
		case reflect.String:
			str := iniconf.DefaultString(fullname, pf.String())
			switch name {
			case "TableFix", "ColumnFix":
				pf.SetString(strings.ToLower(str))
			default:
				pf.SetString(str)
			}

		case reflect.Int, reflect.Int64:
			num := int64(iniconf.DefaultInt64(fullname, pf.Int()))
			switch fullname {
			case "system::maxmemorymb",
				"filecache::cachesecond", "filecache::singlefileallowmb", "filecache::maxcapmb",
				"listen::readtimeout", "listen::writetimeout",
				"session::sessiongcmaxlifetime", "session::sessioncookielifetime",
				"log::asyncchan":
				if num > 0 {
					pf.SetInt(num)
				}
			case "log::level":
				str := logLevelString(int(num))
				str2 := iniconf.DefaultString(fullname, str)
				num = int64(logLevelInt(str2))
				if num != -10 {
					pf.SetInt(num)
					iniconf.Set(fullname, str2)
				} else {
					iniconf.Set(fullname, str)
				}
				continue
			default:
				pf.SetInt(num)
			}

		case reflect.Bool:
			pf.SetBool(iniconf.DefaultBool(fullname, pf.Bool()))

		default:
			continue
		}
		iniconf.Set(fullname, fmt.Sprint(pf.Interface()))
	}
}

func initSingleConfig(section string, p interface{}, iniconf config.Configer) {
	pt := reflect.TypeOf(p)
	if pt.Kind() != reflect.Ptr {
		return
	}
	pt = pt.Elem()
	if pt.Kind() != reflect.Struct {
		return
	}
	pv := reflect.ValueOf(p).Elem()

	for i := 0; i < pt.NumField(); i++ {
		pf := pv.Field(i)
		if !pf.CanSet() {
			continue
		}
		fullname := getfullname(section, pt.Field(i).Name)
		switch pf.Kind() {
		case reflect.String, reflect.Int, reflect.Int64, reflect.Bool:
			switch fullname {
			case "log::level":
				iniconf.Set(fullname, logLevelString(int(pf.Int())))
			default:
				iniconf.Set(fullname, fmt.Sprint(pf.Interface()))
			}
		}
	}
}

// section name and key name case insensitive
func getfullname(section, name string) string {
	if section == "" {
		return strings.ToLower(name)
	}
	return strings.ToLower(section + "::" + name)
}

func logLevelInt(l string) int {
	switch strings.ToLower(l) {
	case "debug":
		return logs.DEBUG
	case "info":
		return logs.INFO
	case "warn":
		return logs.WARN
	case "error":
		return logs.ERROR
	case "fatal":
		return logs.FATAL
	case "off":
		return logs.OFF
	}
	return -10
}

func logLevelString(l int) string {
	switch l {
	case logs.DEBUG:
		return "debug"
	case logs.INFO:
		return "info"
	case logs.WARN:
		return "warn"
	case logs.ERROR:
		return "error"
	case logs.FATAL:
		return "fatal"
	case logs.OFF:
		return "off"
	}
	return "error"
}
<commit_msg>Config配置项自动修复人为造成的冗余项<commit_after>package lessgo

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

	"github.com/lessgo/lessgo/config"
	"github.com/lessgo/lessgo/logs"
	"github.com/lessgo/lessgo/session"
)

type (
	// Config is the main struct for BConfig
	Config struct {
		AppName             string // Application name
		Info                Info   // Application info
		Debug               bool   // enable/disable debug mode.
		CrossDomain         bool
		RouterCaseSensitive bool  // 是否路由忽略大小写匹配，默认是 true，区分大小写
		MaxMemoryMB         int64 // 文件上传默认内存缓存大小，单位MB
		Listen              Listen
		Session             SessionConfig
		Log                 LogConfig
		FileCache           FileCacheConfig
		DefaultDB           string
		DBList              map[string]DBConfig
	}
	Info struct {
		Version           string
		Description       string
		Email             string
		TermsOfServiceUrl string
		License           string
		LicenseUrl        string
	}
	// Listen holds for http and https related config
	Listen struct {
		Graceful      bool // Graceful means use graceful module to start the server
		Address       string
		ReadTimeout   int64
		WriteTimeout  int64
		EnableHTTPS   bool
		HTTPSKeyFile  string
		HTTPSCertFile string
	}
	// SessionConfig holds session related config
	SessionConfig struct {
		SessionOn               bool
		SessionProvider         string
		SessionName             string
		SessionGCMaxLifetime    int64
		SessionProviderConfig   string
		SessionCookieLifeTime   int
		SessionAutoSetCookie    bool
		SessionDomain           string
		EnableSidInHttpHeader   bool //	enable store/get the sessionId into/from http headers
		SessionNameInHttpHeader string
		EnableSidInUrlQuery     bool //	enable get the sessionId from Url Query params
	}

	// LogConfig holds Log related config
	LogConfig struct {
		Level     int
		AsyncChan int64
	}
	FileCacheConfig struct {
		CacheSecond       int64 // 静态资源缓存监测频率与缓存动态释放的最大时长，单位秒，默认600秒
		SingleFileAllowMB int64 // 允许的最大文件，单位MB
		MaxCapMB          int64 // 最大缓存总量，单位MB
	}
	// DataBase connection Config
	DBConfig struct {
		Name         string
		Driver       string // Driver：mssql | odbc(mssql) | mysql | mymysql | postgres | sqlite3 | oci8 | goracle
		ConnString   string
		MaxOpenConns int
		MaxIdleConns int
		TableFix     string // 表命名空间是前缀还是后缀：prefix | suffix
		TableSpace   string // 表命名空间
		TableSnake   bool   // 表名使用snake风格或保持不变
		ColumnFix    string // 列命名空间是前缀还是后缀：prefix | suffix
		ColumnSpace  string // 列命名空间
		ColumnSnake  bool   // 列名使用snake风格或保持不变
		DisableCache bool
		ShowExecTime bool
		ShowSql      bool
	}
)

// 项目固定目录文件名称
const (
	BUSINESS_API_DIR  = "BusinessApi"
	BUSINESS_VIEW_DIR = "BusinessView"
	SYSTEM_API_DIR    = "SystemApi"
	SYSTEM_VIEW_DIR   = "SystemView"
	STATIC_DIR        = "Static"
	IMG_DIR           = STATIC_DIR + "/Img"
	JS_DIR            = STATIC_DIR + "/Js"
	CSS_DIR           = STATIC_DIR + "/Css"
	TPL_DIR           = STATIC_DIR + "/Tpl"
	PLUGIN_DIR        = STATIC_DIR + "/Plugin"
	UPLOADS_DIR       = "Uploads"
	COMMON_DIR        = "Common"
	MIDDLEWARE_DIR    = COMMON_DIR + "/Middleware"

	TPL_EXT         = ".tpl"
	STATIC_HTML_EXT = ".html"

	CONFIG_DIR     = "Config"
	APPCONFIG_FILE = CONFIG_DIR + "/app.config"
	DBCONFIG_FILE  = CONFIG_DIR + "/db.config"

	DB_DIR            = COMMON_DIR + "/DB"
	DEFAULTDB_SECTION = "defaultdb"

	VIEW_PKG      = "/View"
	MODULE_SUFFIX = "Module"
)

var (
	// AppConfig is the instance of Config, store the config information from file
	AppConfig = initConfig()
	// GlobalSessions is the instance for the session manager
	GlobalSessions *session.Manager
)

func initConfig() *Config {
	return &Config{
		AppName: "lessgo",
		Info: Info{
			Version:     "0.4.0",
			Description: "A simple, stable, efficient and flexible web framework.",
			// Host:              "127.0.0.1:8080",
			Email:             "henrylee_cn@foxmail.com",
			TermsOfServiceUrl: "https://github.com/lessgo/lessgo",
			License:           "MIT",
			LicenseUrl:        "https://github.com/lessgo/lessgo/raw/master/doc/LICENSE",
		},
		Debug:               true,
		CrossDomain:         false,
		RouterCaseSensitive: false,
		MaxMemoryMB:         64, // 64MB
		Listen: Listen{
			Graceful:      false,
			Address:       "0.0.0.0:8080",
			ReadTimeout:   0,
			WriteTimeout:  0,
			EnableHTTPS:   false,
			HTTPSCertFile: "",
			HTTPSKeyFile:  "",
		},
		Session: SessionConfig{
			SessionOn:               false,
			SessionProvider:         "memory",
			SessionName:             "lessgosessionID",
			SessionGCMaxLifetime:    3600,
			SessionProviderConfig:   "",
			SessionCookieLifeTime:   0, //set cookie default is the browser life
			SessionAutoSetCookie:    true,
			SessionDomain:           "",
			EnableSidInHttpHeader:   false, //	enable store/get the sessionId into/from http headers
			SessionNameInHttpHeader: "Lessgosessionid",
			EnableSidInUrlQuery:     false, //	enable get the sessionId from Url Query params
		},

		FileCache: FileCacheConfig{
			CacheSecond:       600, // 600s
			SingleFileAllowMB: 64,  // 64MB
			MaxCapMB:          256, // 256MB
		},
		Log: LogConfig{
			Level:     logs.DEBUG,
			AsyncChan: 1000,
		},
		DefaultDB: "lessgo",
		DBList: map[string]DBConfig{
			"lessgo": {
				Name:         "lessgo",
				Driver:       "sqlite3",
				ConnString:   DB_DIR + "/sqlite.db",
				MaxOpenConns: 1,
				MaxIdleConns: 1,
				TableFix:     "prefix",
				TableSpace:   "",
				TableSnake:   true,
				ColumnFix:    "prefix",
				ColumnSpace:  "",
				ColumnSnake:  true,
				DisableCache: false,
				ShowExecTime: false,
				ShowSql:      false,
			},
		},
	}
}

func LoadAppConfig() (err error) {
	fname := APPCONFIG_FILE
	iniconf, err := config.NewConfig("ini", fname)
	if err == nil {
		os.Remove(fname)
		readSingleConfig("system", AppConfig, iniconf)
		readSingleConfig("filecache", &AppConfig.FileCache, iniconf)
		readSingleConfig("info", &AppConfig.Info, iniconf)
		readSingleConfig("listen", &AppConfig.Listen, iniconf)
		readSingleConfig("log", &AppConfig.Log, iniconf)
		readSingleConfig("session", &AppConfig.Session, iniconf)
	}
	os.MkdirAll(filepath.Dir(fname), 0777)
	f, err := os.Create(fname)
	if err != nil {
		return err
	}
	f.Close()
	iniconf, err = config.NewConfig("ini", fname)
	if err != nil {
		return err
	}
	writeSingleConfig("system", AppConfig, iniconf)
	writeSingleConfig("filecache", &AppConfig.FileCache, iniconf)
	writeSingleConfig("info", &AppConfig.Info, iniconf)
	writeSingleConfig("listen", &AppConfig.Listen, iniconf)
	writeSingleConfig("log", &AppConfig.Log, iniconf)
	writeSingleConfig("session", &AppConfig.Session, iniconf)

	return iniconf.SaveConfigFile(fname)
}

func LoadDBConfig() (err error) {
	fname := DBCONFIG_FILE
	iniconf, err := config.NewConfig("ini", fname)
	if err == nil {
		os.Remove(fname)
		defDB := AppConfig.DBList["lessgo"]
		for _, section := range iniconf.(*config.IniConfigContainer).Sections() {
			dbconfig := defDB
			readSingleConfig(section, &dbconfig, iniconf)
			if strings.ToLower(section) == DEFAULTDB_SECTION {
				AppConfig.DefaultDB = dbconfig.Name
			}
			AppConfig.DBList[dbconfig.Name] = dbconfig
		}
	}

	os.MkdirAll(filepath.Dir(fname), 0777)
	f, err := os.Create(fname)
	if err != nil {
		return err
	}
	f.Close()
	iniconf, err = config.NewConfig("ini", fname)
	if err != nil {
		return err
	}
	for _, dbconfig := range AppConfig.DBList {
		if AppConfig.DefaultDB == dbconfig.Name {
			writeSingleConfig(DEFAULTDB_SECTION, &dbconfig, iniconf)
		} else {
			writeSingleConfig(dbconfig.Name, &dbconfig, iniconf)
		}
	}

	return iniconf.SaveConfigFile(fname)
}

func readSingleConfig(section string, p interface{}, iniconf config.Configer) {
	pt := reflect.TypeOf(p)
	if pt.Kind() != reflect.Ptr {
		return
	}
	pt = pt.Elem()
	if pt.Kind() != reflect.Struct {
		return
	}
	pv := reflect.ValueOf(p).Elem()

	for i := 0; i < pt.NumField(); i++ {
		pf := pv.Field(i)
		if !pf.CanSet() {
			continue
		}
		name := pt.Field(i).Name
		fullname := getfullname(section, name)
		switch pf.Kind() {
		case reflect.String:
			str := iniconf.DefaultString(fullname, pf.String())
			switch name {
			case "TableFix", "ColumnFix":
				pf.SetString(strings.ToLower(str))
			default:
				pf.SetString(str)
			}

		case reflect.Int, reflect.Int64:
			num := int64(iniconf.DefaultInt64(fullname, pf.Int()))
			switch fullname {
			case "system::maxmemorymb",
				"filecache::cachesecond", "filecache::singlefileallowmb", "filecache::maxcapmb",
				"listen::readtimeout", "listen::writetimeout",
				"session::sessiongcmaxlifetime", "session::sessioncookielifetime",
				"log::asyncchan":
				if num > 0 {
					pf.SetInt(num)
				}
			case "log::level":
				str := logLevelString(int(num))
				str2 := iniconf.DefaultString(fullname, str)
				num = int64(logLevelInt(str2))
				if num != -10 {
					pf.SetInt(num)
				}
			default:
				pf.SetInt(num)
			}

		case reflect.Bool:
			pf.SetBool(iniconf.DefaultBool(fullname, pf.Bool()))
		}
	}
}

func writeSingleConfig(section string, p interface{}, iniconf config.Configer) {
	pt := reflect.TypeOf(p)
	if pt.Kind() != reflect.Ptr {
		return
	}
	pt = pt.Elem()
	if pt.Kind() != reflect.Struct {
		return
	}
	pv := reflect.ValueOf(p).Elem()

	for i := 0; i < pt.NumField(); i++ {
		pf := pv.Field(i)
		if !pf.CanSet() {
			continue
		}
		fullname := getfullname(section, pt.Field(i).Name)
		switch pf.Kind() {
		case reflect.String, reflect.Int, reflect.Int64, reflect.Bool:
			switch fullname {
			case "log::level":
				iniconf.Set(fullname, logLevelString(int(pf.Int())))
			default:
				iniconf.Set(fullname, fmt.Sprint(pf.Interface()))
			}
		}
	}
}

// section name and key name case insensitive
func getfullname(section, name string) string {
	if section == "" {
		return strings.ToLower(name)
	}
	return strings.ToLower(section + "::" + name)
}

func logLevelInt(l string) int {
	switch strings.ToLower(l) {
	case "debug":
		return logs.DEBUG
	case "info":
		return logs.INFO
	case "warn":
		return logs.WARN
	case "error":
		return logs.ERROR
	case "fatal":
		return logs.FATAL
	case "off":
		return logs.OFF
	}
	return -10
}

func logLevelString(l int) string {
	switch l {
	case logs.DEBUG:
		return "debug"
	case logs.INFO:
		return "info"
	case logs.WARN:
		return "warn"
	case logs.ERROR:
		return "error"
	case logs.FATAL:
		return "fatal"
	case logs.OFF:
		return "off"
	}
	return "error"
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Trygve Aaberge
// Released under the MIT License, http://opensource.org/licenses/MIT

package main

import (
	"bufio"
	"fmt"
	"os"
	"reflect"
	"strconv"
	"strings"
)

type Config struct {
	Server  string
	Channel string
	Nr      int
	IPv6    bool
}

func loadConfig(filename string) (config Config, err error) {
	configReflect := reflect.ValueOf(&config).Elem()

	f, err := os.Open(filename)
	if err != nil {
		return config, err
	}
	fh := bufio.NewScanner(f)

	for fh.Scan() {
		lineParts := strings.Split(fh.Text(), "\"")
		line := ""
		for k, v := range lineParts {
			if k%2 == 0 {
				if i := strings.Index(v, "#"); i != -1 {
					line += v[:i]
					break
				}
			}
			line += v
		}

		if strings.TrimSpace(line) == "" {
			continue
		}

		keyVal := strings.SplitN(line, "=", 2)
		if len(keyVal) < 2 {
			return config, fmt.Errorf("Config line must contain \"=\": %s", line)
		}
		key := strings.TrimSpace(keyVal[0])
		value := strings.TrimSpace(keyVal[1])

		field := configReflect.FieldByName(key)
		if !field.IsValid() {
			return config, fmt.Errorf("Config key is not valid: %s", key)
		}

		switch field.Kind() {
		case reflect.String:
			field.SetString(value)
		case reflect.Int:
			i, err := strconv.ParseInt(value, 10, 64)
			if err != nil {
				return config, fmt.Errorf("Invalid int \"%s\" in key \"%s\": %s", value, key, err)
			}
			field.SetInt(i)
		case reflect.Bool:
			if value != "true" && value != "false" {
				return config, fmt.Errorf("Invalid bool \"%s\" in key \"%s\": must be true or false", value, key)
			}
			field.SetBool(value == "true")
		}
	}

	return config, nil
}

func main() {
	config, err := loadConfig("itkbot.conf")
	if err != nil {
		fmt.Println("Couldn't load config:", err)
		return
	}

	fmt.Println("Server:", config.Server)
	fmt.Println("Channel:", config.Channel)
	fmt.Println("Nr:", config.Nr)
	fmt.Println("IPv6:", config.IPv6)
}
<commit_msg>Use strconv.ParseBool<commit_after>// Copyright (c) 2014 Trygve Aaberge
// Released under the MIT License, http://opensource.org/licenses/MIT

package main

import (
	"bufio"
	"fmt"
	"os"
	"reflect"
	"strconv"
	"strings"
)

type Config struct {
	Server  string
	Channel string
	Nr      int
	IPv6    bool
}

func loadConfig(filename string) (config Config, err error) {
	configReflect := reflect.ValueOf(&config).Elem()

	f, err := os.Open(filename)
	if err != nil {
		return config, err
	}
	fh := bufio.NewScanner(f)

	for fh.Scan() {
		lineParts := strings.Split(fh.Text(), "\"")
		line := ""
		for k, v := range lineParts {
			if k%2 == 0 {
				if i := strings.Index(v, "#"); i != -1 {
					line += v[:i]
					break
				}
			}
			line += v
		}

		if strings.TrimSpace(line) == "" {
			continue
		}

		keyVal := strings.SplitN(line, "=", 2)
		if len(keyVal) < 2 {
			return config, fmt.Errorf("Config line must contain \"=\": %s", line)
		}
		key := strings.TrimSpace(keyVal[0])
		value := strings.TrimSpace(keyVal[1])

		field := configReflect.FieldByName(key)
		if !field.IsValid() {
			return config, fmt.Errorf("Config key is not valid: %s", key)
		}

		switch field.Kind() {
		case reflect.String:
			field.SetString(value)
		case reflect.Int:
			i, err := strconv.ParseInt(value, 10, 64)
			if err != nil {
				return config, fmt.Errorf("Invalid int \"%s\" in key \"%s\": %s", value, key, err)
			}
			field.SetInt(i)
		case reflect.Bool:
			v, err := strconv.ParseBool(value)
			if err != nil {
				return config, fmt.Errorf("Invalid bool \"%s\" in key \"%s\": %s", value, key, err)
			}
			field.SetBool(v)
		}
	}

	return config, nil
}

func main() {
	config, err := loadConfig("itkbot.conf")
	if err != nil {
		fmt.Println("Couldn't load config:", err)
		return
	}

	fmt.Println("Server:", config.Server)
	fmt.Println("Channel:", config.Channel)
	fmt.Println("Nr:", config.Nr)
	fmt.Println("IPv6:", config.IPv6)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"runtime"
)

const (
	// Name is the user facing name for this binary. Internally we call it
	// klientctl to avoid confusion.
	Name = "kd"

	// KlientName is the user facing name for klient.
	KlientName = "KD Daemon"

	// KlientAddress is url of locally running klient to connect to send
	// user commands.
	KlientAddress = "http://127.0.0.1:56789/kite"

	// KiteHome is full path to the kite key that we will use to authenticate
	// to the given klient.
	KiteHome = "/etc/kite"

	// KlientDirectory is full path to directory that holds klient.
	KlientDirectory = "/opt/kite/klient"

	// KlientctlDirectory is full path to directory that holds klientctl.
	KlientctlDirectory = "/usr/local/bin"

	// KlientctlBinName is the bin named that will be stored in the KlientctlDirectory.
	KlientctlBinName = "kd"

	// KontrolURL is the url to connect to authenticate local klient and get
	// list of machines.
	KontrolURL = "https://koding.com/kontrol/kite"

	// Version is the current version of klientctl. This number is used
	// by CheckUpdate to determine if current version is behind or equal to latest
	// version on S3 bucket.
	Version = 11

	osName = runtime.GOOS

	// S3UpdateLocation is publically accessible url to check for new updates.
	S3UpdateLocation = "https://koding-kd.s3.amazonaws.com/latest-version.txt"

	// S3KlientPath is publically accessible url for latest version of klient.
	// Each OS has its own version of binary, identifiable by OS suffix.
	S3KlientPath = "https://koding-kd.s3.amazonaws.com/klient-" + osName

	// S3KlientctlPath is publically accessible url for latest version of
	// klientctl. Each OS has its own version of binary, identifiable by suffix.
	S3KlientctlPath = "https://koding-kd.s3.amazonaws.com/klientctl-" + osName

	// SSHDefaultKeyDir is the default directory that stores users ssh key pairs.
	SSHDefaultKeyDir = ".ssh"

	// SSHDefaultKeyName is the default name of the ssh key pair.
	SSHDefaultKeyName = "kd-ssh-key"
)

// KiteVersion is the version identifier used to connect to Kontrol.
var KiteVersion = fmt.Sprintf("0.0.%d", Version)
<commit_msg>Bump version to 12<commit_after>package main

import (
	"fmt"
	"runtime"
)

const (
	// Name is the user facing name for this binary. Internally we call it
	// klientctl to avoid confusion.
	Name = "kd"

	// KlientName is the user facing name for klient.
	KlientName = "KD Daemon"

	// KlientAddress is url of locally running klient to connect to send
	// user commands.
	KlientAddress = "http://127.0.0.1:56789/kite"

	// KiteHome is full path to the kite key that we will use to authenticate
	// to the given klient.
	KiteHome = "/etc/kite"

	// KlientDirectory is full path to directory that holds klient.
	KlientDirectory = "/opt/kite/klient"

	// KlientctlDirectory is full path to directory that holds klientctl.
	KlientctlDirectory = "/usr/local/bin"

	// KlientctlBinName is the bin named that will be stored in the KlientctlDirectory.
	KlientctlBinName = "kd"

	// KontrolURL is the url to connect to authenticate local klient and get
	// list of machines.
	KontrolURL = "https://koding.com/kontrol/kite"

	// Version is the current version of klientctl. This number is used
	// by CheckUpdate to determine if current version is behind or equal to latest
	// version on S3 bucket.
	Version = 12

	osName = runtime.GOOS

	// S3UpdateLocation is publically accessible url to check for new updates.
	S3UpdateLocation = "https://koding-kd.s3.amazonaws.com/latest-version.txt"

	// S3KlientPath is publically accessible url for latest version of klient.
	// Each OS has its own version of binary, identifiable by OS suffix.
	S3KlientPath = "https://koding-kd.s3.amazonaws.com/klient-" + osName

	// S3KlientctlPath is publically accessible url for latest version of
	// klientctl. Each OS has its own version of binary, identifiable by suffix.
	S3KlientctlPath = "https://koding-kd.s3.amazonaws.com/klientctl-" + osName

	// SSHDefaultKeyDir is the default directory that stores users ssh key pairs.
	SSHDefaultKeyDir = ".ssh"

	// SSHDefaultKeyName is the default name of the ssh key pair.
	SSHDefaultKeyName = "kd-ssh-key"
)

// KiteVersion is the version identifier used to connect to Kontrol.
var KiteVersion = fmt.Sprintf("0.0.%d", Version)
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/url"
	"strconv"
	"strings"

	"github.com/agl/xmpp-client/xmpp"
	"golang.org/x/crypto/otr"
	"golang.org/x/crypto/ssh/terminal"
	"golang.org/x/net/proxy"
)

type Config struct {
	filename                      string `json:"-"`
	Account                       string
	Server                        string   `json:",omitempty"`
	Proxies                       []string `json:",omitempty"`
	Password                      string   `json:",omitempty"`
	Port                          int      `json:",omitempty"`
	PrivateKey                    []byte
	KnownFingerprints             []KnownFingerprint
	RawLogFile                    string   `json:",omitempty"`
	NotifyCommand                 []string `json:",omitempty"`
	IdleSecondsBeforeNotification int      `json:",omitempty"`
	Bell                          bool
	HideStatusUpdates             bool
	UseTor                        bool
	OTRAutoTearDown               bool
	OTRAutoAppendTag              bool
	OTRAutoStartSession           bool
	ServerCertificateSHA256       string   `json:",omitempty"`
	AlwaysEncrypt                 bool     `json:",omitempty"`
	AlwaysEncryptWith             []string `json:",omitempty"`
}

type KnownFingerprint struct {
	UserId         string
	FingerprintHex string
	fingerprint    []byte `json:"-"`
}

func ParseConfig(filename string) (c *Config, err error) {
	contents, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}

	c = new(Config)
	if err = json.Unmarshal(contents, &c); err != nil {
		return
	}

	c.filename = filename

	for i, known := range c.KnownFingerprints {
		c.KnownFingerprints[i].fingerprint, err = hex.DecodeString(known.FingerprintHex)
		if err != nil {
			err = errors.New("xmpp: failed to parse hex fingerprint for " + known.UserId + ": " + err.Error())
			return
		}
	}

	return
}

func (c *Config) Save() error {
	for i, known := range c.KnownFingerprints {
		c.KnownFingerprints[i].FingerprintHex = hex.EncodeToString(known.fingerprint)
	}

	contents, err := json.MarshalIndent(c, "", "\t")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(c.filename, contents, 0600)
}

func (c *Config) UserIdForFingerprint(fpr []byte) string {
	for _, known := range c.KnownFingerprints {
		if bytes.Equal(fpr, known.fingerprint) {
			return known.UserId
		}
	}

	return ""
}

func (c *Config) HasFingerprint(uid string) bool {
	for _, known := range c.KnownFingerprints {
		if uid == known.UserId {
			return true
		}
	}

	return false
}

func (c *Config) ShouldEncryptTo(uid string) bool {
	if c.AlwaysEncrypt {
		return true
	}

	for _, contact := range c.AlwaysEncryptWith {
		if contact == uid {
			return true
		}
	}
	return false
}

func enroll(config *Config, term *terminal.Terminal) bool {
	var err error
	warn(term, "Enrolling new config file")

	var domain string
	for {
		term.SetPrompt("Account (i.e. user@example.com, enter to quit): ")
		if config.Account, err = term.ReadLine(); err != nil || len(config.Account) == 0 {
			return false
		}

		parts := strings.SplitN(config.Account, "@", 2)
		if len(parts) != 2 {
			alert(term, "invalid username (want user@domain): "+config.Account)
			continue
		}
		domain = parts[1]
		break
	}

	term.SetPrompt("Enable debug logging to /tmp/xmpp-client-debug.log? ")
	if debugLog, err := term.ReadLine(); err != nil || debugLog != "yes" {
		info(term, "Not enabling debug logging...")
	} else {
		info(term, "Debug logging enabled...")
		config.RawLogFile = "/tmp/xmpp-client-debug.log"
	}

	term.SetPrompt("Use Tor?: ")
	if useTorQuery, err := term.ReadLine(); err != nil || len(useTorQuery) == 0 || useTorQuery[0] != 'y' && useTorQuery[0] != 'Y' {
		info(term, "Not using Tor...")
		config.UseTor = false
	} else {
		info(term, "Using Tor...")
		config.UseTor = true
	}

	term.SetPrompt("File to import libotr private key from (enter to generate): ")

	var priv otr.PrivateKey
	for {
		importFile, err := term.ReadLine()
		if err != nil {
			return false
		}
		if len(importFile) > 0 {
			privKeyBytes, err := ioutil.ReadFile(importFile)
			if err != nil {
				alert(term, "Failed to open private key file: "+err.Error())
				continue
			}

			if !priv.Import(privKeyBytes) {
				alert(term, "Failed to parse libotr private key file (the parser is pretty simple I'm afraid)")
				continue
			}
			break
		} else {
			info(term, "Generating private key...")
			priv.Generate(rand.Reader)
			break
		}
	}
	config.PrivateKey = priv.Serialize(nil)

	config.OTRAutoAppendTag = true
	config.OTRAutoStartSession = true
	config.OTRAutoTearDown = false

	// List well known Tor hidden services.
	knownTorDomain := map[string]string{
		"jabber.ccc.de":             "okj7xc6j2szr2y75.onion",
		"riseup.net":                "4cjw6cwpeaeppfqz.onion",
		"jabber.calyxinstitute.org": "ijeeynrc6x2uy5ob.onion",
		"jabber.otr.im":             "5rgdtlawqkcplz75.onion",
		"wtfismyip.com":             "ofkztxcohimx34la.onion",
	}

	// Autoconfigure well known Tor hidden services.
	if hiddenService, ok := knownTorDomain[domain]; ok && config.UseTor {
		const torProxyURL = "socks5://127.0.0.1:9050"
		info(term, "It appears that you are using a well known server and we will use its Tor hidden service to connect.")
		config.Server = hiddenService
		config.Port = 5222
		config.Proxies = []string{torProxyURL}
		term.SetPrompt("> ")
		return true
	}

	var proxyStr string
	proxyDefaultPrompt := ", enter for none"
	if config.UseTor {
		proxyDefaultPrompt = ", which is the default"
	}
	term.SetPrompt("Proxy (i.e socks5://127.0.0.1:9050" + proxyDefaultPrompt + "): ")

	for {
		if proxyStr, err = term.ReadLine(); err != nil {
			return false
		}
		if len(proxyStr) == 0 {
			if !config.UseTor {
				break
			} else {
				proxyStr = "socks5://127.0.0.1:9050"
			}
		}
		u, err := url.Parse(proxyStr)
		if err != nil {
			alert(term, "Failed to parse "+proxyStr+" as a URL: "+err.Error())
			continue
		}
		if _, err = proxy.FromURL(u, proxy.Direct); err != nil {
			alert(term, "Failed to parse "+proxyStr+" as a proxy: "+err.Error())
			continue
		}
		break
	}

	if len(proxyStr) > 0 {
		config.Proxies = []string{proxyStr}

		info(term, "Since you selected a proxy, we need to know the server and port to connect to as a SRV lookup would leak information every time.")
		term.SetPrompt("Server (i.e. xmpp.example.com, enter to lookup using unproxied DNS): ")
		if config.Server, err = term.ReadLine(); err != nil {
			return false
		}
		if len(config.Server) == 0 {
			var port uint16
			info(term, "Performing SRV lookup")
			if config.Server, port, err = xmpp.Resolve(domain); err != nil {
				alert(term, "SRV lookup failed: "+err.Error())
				return false
			}
			config.Port = int(port)
			info(term, "Resolved "+config.Server+":"+strconv.Itoa(config.Port))
		} else {
			for {
				term.SetPrompt("Port (enter for 5222): ")
				portStr, err := term.ReadLine()
				if err != nil {
					return false
				}
				if len(portStr) == 0 {
					portStr = "5222"
				}
				if config.Port, err = strconv.Atoi(portStr); err != nil || config.Port <= 0 || config.Port > 65535 {
					info(term, "Port numbers must be 0 < port <= 65535")
					continue
				}
				break
			}
		}
	}

	term.SetPrompt("> ")

	return true
}
<commit_msg>Add "rows.io" as well known service<commit_after>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/url"
	"strconv"
	"strings"

	"github.com/agl/xmpp-client/xmpp"
	"golang.org/x/crypto/otr"
	"golang.org/x/crypto/ssh/terminal"
	"golang.org/x/net/proxy"
)

type Config struct {
	filename                      string `json:"-"`
	Account                       string
	Server                        string   `json:",omitempty"`
	Proxies                       []string `json:",omitempty"`
	Password                      string   `json:",omitempty"`
	Port                          int      `json:",omitempty"`
	PrivateKey                    []byte
	KnownFingerprints             []KnownFingerprint
	RawLogFile                    string   `json:",omitempty"`
	NotifyCommand                 []string `json:",omitempty"`
	IdleSecondsBeforeNotification int      `json:",omitempty"`
	Bell                          bool
	HideStatusUpdates             bool
	UseTor                        bool
	OTRAutoTearDown               bool
	OTRAutoAppendTag              bool
	OTRAutoStartSession           bool
	ServerCertificateSHA256       string   `json:",omitempty"`
	AlwaysEncrypt                 bool     `json:",omitempty"`
	AlwaysEncryptWith             []string `json:",omitempty"`
}

type KnownFingerprint struct {
	UserId         string
	FingerprintHex string
	fingerprint    []byte `json:"-"`
}

func ParseConfig(filename string) (c *Config, err error) {
	contents, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}

	c = new(Config)
	if err = json.Unmarshal(contents, &c); err != nil {
		return
	}

	c.filename = filename

	for i, known := range c.KnownFingerprints {
		c.KnownFingerprints[i].fingerprint, err = hex.DecodeString(known.FingerprintHex)
		if err != nil {
			err = errors.New("xmpp: failed to parse hex fingerprint for " + known.UserId + ": " + err.Error())
			return
		}
	}

	return
}

func (c *Config) Save() error {
	for i, known := range c.KnownFingerprints {
		c.KnownFingerprints[i].FingerprintHex = hex.EncodeToString(known.fingerprint)
	}

	contents, err := json.MarshalIndent(c, "", "\t")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(c.filename, contents, 0600)
}

func (c *Config) UserIdForFingerprint(fpr []byte) string {
	for _, known := range c.KnownFingerprints {
		if bytes.Equal(fpr, known.fingerprint) {
			return known.UserId
		}
	}

	return ""
}

func (c *Config) HasFingerprint(uid string) bool {
	for _, known := range c.KnownFingerprints {
		if uid == known.UserId {
			return true
		}
	}

	return false
}

func (c *Config) ShouldEncryptTo(uid string) bool {
	if c.AlwaysEncrypt {
		return true
	}

	for _, contact := range c.AlwaysEncryptWith {
		if contact == uid {
			return true
		}
	}
	return false
}

func enroll(config *Config, term *terminal.Terminal) bool {
	var err error
	warn(term, "Enrolling new config file")

	var domain string
	for {
		term.SetPrompt("Account (i.e. user@example.com, enter to quit): ")
		if config.Account, err = term.ReadLine(); err != nil || len(config.Account) == 0 {
			return false
		}

		parts := strings.SplitN(config.Account, "@", 2)
		if len(parts) != 2 {
			alert(term, "invalid username (want user@domain): "+config.Account)
			continue
		}
		domain = parts[1]
		break
	}

	term.SetPrompt("Enable debug logging to /tmp/xmpp-client-debug.log? ")
	if debugLog, err := term.ReadLine(); err != nil || debugLog != "yes" {
		info(term, "Not enabling debug logging...")
	} else {
		info(term, "Debug logging enabled...")
		config.RawLogFile = "/tmp/xmpp-client-debug.log"
	}

	term.SetPrompt("Use Tor?: ")
	if useTorQuery, err := term.ReadLine(); err != nil || len(useTorQuery) == 0 || useTorQuery[0] != 'y' && useTorQuery[0] != 'Y' {
		info(term, "Not using Tor...")
		config.UseTor = false
	} else {
		info(term, "Using Tor...")
		config.UseTor = true
	}

	term.SetPrompt("File to import libotr private key from (enter to generate): ")

	var priv otr.PrivateKey
	for {
		importFile, err := term.ReadLine()
		if err != nil {
			return false
		}
		if len(importFile) > 0 {
			privKeyBytes, err := ioutil.ReadFile(importFile)
			if err != nil {
				alert(term, "Failed to open private key file: "+err.Error())
				continue
			}

			if !priv.Import(privKeyBytes) {
				alert(term, "Failed to parse libotr private key file (the parser is pretty simple I'm afraid)")
				continue
			}
			break
		} else {
			info(term, "Generating private key...")
			priv.Generate(rand.Reader)
			break
		}
	}
	config.PrivateKey = priv.Serialize(nil)

	config.OTRAutoAppendTag = true
	config.OTRAutoStartSession = true
	config.OTRAutoTearDown = false

	// List well known Tor hidden services.
	knownTorDomain := map[string]string{
		"jabber.ccc.de":             "okj7xc6j2szr2y75.onion",
		"riseup.net":                "4cjw6cwpeaeppfqz.onion",
		"jabber.calyxinstitute.org": "ijeeynrc6x2uy5ob.onion",
		"jabber.otr.im":             "5rgdtlawqkcplz75.onion",
		"wtfismyip.com":             "ofkztxcohimx34la.onion",
		"rows.io":                   "yz6yiv2hxyagvwy6.onion",
	}

	// Autoconfigure well known Tor hidden services.
	if hiddenService, ok := knownTorDomain[domain]; ok && config.UseTor {
		const torProxyURL = "socks5://127.0.0.1:9050"
		info(term, "It appears that you are using a well known server and we will use its Tor hidden service to connect.")
		config.Server = hiddenService
		config.Port = 5222
		config.Proxies = []string{torProxyURL}
		term.SetPrompt("> ")
		return true
	}

	var proxyStr string
	proxyDefaultPrompt := ", enter for none"
	if config.UseTor {
		proxyDefaultPrompt = ", which is the default"
	}
	term.SetPrompt("Proxy (i.e socks5://127.0.0.1:9050" + proxyDefaultPrompt + "): ")

	for {
		if proxyStr, err = term.ReadLine(); err != nil {
			return false
		}
		if len(proxyStr) == 0 {
			if !config.UseTor {
				break
			} else {
				proxyStr = "socks5://127.0.0.1:9050"
			}
		}
		u, err := url.Parse(proxyStr)
		if err != nil {
			alert(term, "Failed to parse "+proxyStr+" as a URL: "+err.Error())
			continue
		}
		if _, err = proxy.FromURL(u, proxy.Direct); err != nil {
			alert(term, "Failed to parse "+proxyStr+" as a proxy: "+err.Error())
			continue
		}
		break
	}

	if len(proxyStr) > 0 {
		config.Proxies = []string{proxyStr}

		info(term, "Since you selected a proxy, we need to know the server and port to connect to as a SRV lookup would leak information every time.")
		term.SetPrompt("Server (i.e. xmpp.example.com, enter to lookup using unproxied DNS): ")
		if config.Server, err = term.ReadLine(); err != nil {
			return false
		}
		if len(config.Server) == 0 {
			var port uint16
			info(term, "Performing SRV lookup")
			if config.Server, port, err = xmpp.Resolve(domain); err != nil {
				alert(term, "SRV lookup failed: "+err.Error())
				return false
			}
			config.Port = int(port)
			info(term, "Resolved "+config.Server+":"+strconv.Itoa(config.Port))
		} else {
			for {
				term.SetPrompt("Port (enter for 5222): ")
				portStr, err := term.ReadLine()
				if err != nil {
					return false
				}
				if len(portStr) == 0 {
					portStr = "5222"
				}
				if config.Port, err = strconv.Atoi(portStr); err != nil || config.Port <= 0 || config.Port > 65535 {
					info(term, "Port numbers must be 0 < port <= 65535")
					continue
				}
				break
			}
		}
	}

	term.SetPrompt("> ")

	return true
}
<|endoftext|>


<commit_before>package types

import (
	"fmt"
	"reflect"
	"time"

	abci "github.com/tendermint/abci/types"
	crypto "github.com/tendermint/go-crypto"
)

//-------------------------------------------------------
// Use strings to distinguish types in ABCI messages

const (
	ABCIEvidenceTypeDuplicateVote = "duplicate/vote"
	ABCIEvidenceTypeMockGood      = "mock/good"
)

const (
	ABCIPubKeyTypeEd25519   = "ed25519"
	ABCIPubKeyTypeSecp256k1 = "secp256k1"
)

//-------------------------------------------------------

// TM2PB is used for converting Tendermint ABCI to protobuf ABCI.
// UNSTABLE
var TM2PB = tm2pb{}

type tm2pb struct{}

func (tm2pb) Header(header *Header) abci.Header {
	return abci.Header{
		ChainID:        header.ChainID,
		Height:         header.Height,
		Time:           header.Time.Unix(),
		NumTxs:         int32(header.NumTxs), // XXX: overflow
		LastBlockHash:  header.LastBlockID.Hash,
		ValidatorsHash: header.ValidatorsHash,
		AppHash:        header.AppHash,
	}
}

func (tm2pb) Validator(val *Validator) abci.Validator {
	return abci.Validator{
		PubKey: TM2PB.PubKey(val.PubKey),
		Power:  val.VotingPower,
	}
}

func (tm2pb) PubKey(pubKey crypto.PubKey) abci.PubKey {
	switch pk := pubKey.(type) {
	case crypto.PubKeyEd25519:
		return abci.PubKey{
			Type: ABCIPubKeyTypeEd25519,
			Data: pk[:],
		}
	case crypto.PubKeySecp256k1:
		return abci.PubKey{
			Type: ABCIPubKeyTypeSecp256k1,
			Data: pk[:],
		}
	default:
		panic(fmt.Sprintf("unknown pubkey type: %v %v", pubKey, reflect.TypeOf(pubKey)))
	}
}

func (tm2pb) Validators(vals *ValidatorSet) []abci.Validator {
	validators := make([]abci.Validator, len(vals.Validators))
	for i, val := range vals.Validators {
		validators[i] = TM2PB.Validator(val)
	}
	return validators
}

func (tm2pb) ConsensusParams(params *ConsensusParams) *abci.ConsensusParams {
	return &abci.ConsensusParams{
		BlockSize: &abci.BlockSize{

			MaxBytes: int32(params.BlockSize.MaxBytes),
			MaxTxs:   int32(params.BlockSize.MaxTxs),
			MaxGas:   params.BlockSize.MaxGas,
		},
		TxSize: &abci.TxSize{
			MaxBytes: int32(params.TxSize.MaxBytes),
			MaxGas:   params.TxSize.MaxGas,
		},
		BlockGossip: &abci.BlockGossip{
			BlockPartSizeBytes: int32(params.BlockGossip.BlockPartSizeBytes),
		},
	}
}

// ABCI Evidence includes information from the past that's not included in the evidence itself
// so Evidence types stays compact.
func (tm2pb) Evidence(ev Evidence, valSet *ValidatorSet, evTime time.Time) abci.Evidence {
	_, val := valSet.GetByAddress(ev.Address())
	if val == nil {
		// should already have checked this
		panic(val)
	}

	abciEvidence := abci.Evidence{
		Validator: abci.Validator{
			Address: ev.Address(),
			PubKey:  TM2PB.PubKey(val.PubKey),
			Power:   val.VotingPower,
		},
		Height:           ev.Height(),
		Time:             evTime.Unix(),
		TotalVotingPower: valSet.TotalVotingPower(),
	}

	// set type
	switch ev.(type) {
	case *DuplicateVoteEvidence:
		abciEvidence.Type = ABCIEvidenceTypeDuplicateVote
	case *MockGoodEvidence, MockGoodEvidence:
		abciEvidence.Type = ABCIEvidenceTypeMockGood
	default:
		panic(fmt.Sprintf("Unknown evidence type: %v %v", ev, reflect.TypeOf(ev)))
	}

	return abciEvidence
}

func (tm2pb) ValidatorFromPubKeyAndPower(pubkey crypto.PubKey, power int64) abci.Validator {
	pubkeyABCI := TM2PB.PubKey(pubkey)
	return abci.Validator{
		Address: pubkey.Address(),
		PubKey:  pubkeyABCI,
		Power:   power,
	}
}

//----------------------------------------------------------------------------

// PB2TM is used for converting protobuf ABCI to Tendermint ABCI.
// UNSTABLE
var PB2TM = pb2tm{}

type pb2tm struct{}

func (pb2tm) PubKey(pubKey abci.PubKey) (crypto.PubKey, error) {
	// TODO: define these in go-crypto and use them
	sizeEd := 32
	sizeSecp := 33
	switch pubKey.Type {
	case ABCIPubKeyTypeEd25519:
		if len(pubKey.Data) != sizeEd {
			return nil, fmt.Errorf("Invalid size for PubKeyEd25519. Got %d, expected %d", len(pubKey.Data), sizeEd)
		}
		var pk crypto.PubKeyEd25519
		copy(pk[:], pubKey.Data)
		return pk, nil
	case ABCIPubKeyTypeSecp256k1:
		if len(pubKey.Data) != sizeSecp {
			return nil, fmt.Errorf("Invalid size for PubKeyEd25519. Got %d, expected %d", len(pubKey.Data), sizeSecp)
		}
		var pk crypto.PubKeySecp256k1
		copy(pk[:], pubKey.Data)
		return pk, nil
	default:
		return nil, fmt.Errorf("Unknown pubkey type %v", pubKey.Type)
	}
}
<commit_msg>use all fields in abci types<commit_after>package types

import (
	"fmt"
	"reflect"
	"time"

	abci "github.com/tendermint/abci/types"
	crypto "github.com/tendermint/go-crypto"
)

//-------------------------------------------------------
// Use strings to distinguish types in ABCI messages

const (
	ABCIEvidenceTypeDuplicateVote = "duplicate/vote"
	ABCIEvidenceTypeMockGood      = "mock/good"
)

const (
	ABCIPubKeyTypeEd25519   = "ed25519"
	ABCIPubKeyTypeSecp256k1 = "secp256k1"
)

//-------------------------------------------------------

// TM2PB is used for converting Tendermint ABCI to protobuf ABCI.
// UNSTABLE
var TM2PB = tm2pb{}

type tm2pb struct{}

func (tm2pb) Header(header *Header) abci.Header {
	return abci.Header{
		ChainID: header.ChainID,
		Height:  header.Height,

		Time:     header.Time.Unix(),
		NumTxs:   int32(header.NumTxs), // XXX: overflow
		TotalTxs: header.NumTxs,

		LastBlockHash:  header.LastBlockID.Hash,
		ValidatorsHash: header.ValidatorsHash,
		AppHash:        header.AppHash,

		// Proposer: TODO
	}
}

func (tm2pb) Validator(val *Validator) abci.Validator {
	return abci.Validator{
		Address: val.PubKey.Address(),
		PubKey:  TM2PB.PubKey(val.PubKey),
		Power:   val.VotingPower,
	}
}

func (tm2pb) PubKey(pubKey crypto.PubKey) abci.PubKey {
	switch pk := pubKey.(type) {
	case crypto.PubKeyEd25519:
		return abci.PubKey{
			Type: ABCIPubKeyTypeEd25519,
			Data: pk[:],
		}
	case crypto.PubKeySecp256k1:
		return abci.PubKey{
			Type: ABCIPubKeyTypeSecp256k1,
			Data: pk[:],
		}
	default:
		panic(fmt.Sprintf("unknown pubkey type: %v %v", pubKey, reflect.TypeOf(pubKey)))
	}
}

func (tm2pb) Validators(vals *ValidatorSet) []abci.Validator {
	validators := make([]abci.Validator, len(vals.Validators))
	for i, val := range vals.Validators {
		validators[i] = TM2PB.Validator(val)
	}
	return validators
}

func (tm2pb) ConsensusParams(params *ConsensusParams) *abci.ConsensusParams {
	return &abci.ConsensusParams{
		BlockSize: &abci.BlockSize{

			MaxBytes: int32(params.BlockSize.MaxBytes),
			MaxTxs:   int32(params.BlockSize.MaxTxs),
			MaxGas:   params.BlockSize.MaxGas,
		},
		TxSize: &abci.TxSize{
			MaxBytes: int32(params.TxSize.MaxBytes),
			MaxGas:   params.TxSize.MaxGas,
		},
		BlockGossip: &abci.BlockGossip{
			BlockPartSizeBytes: int32(params.BlockGossip.BlockPartSizeBytes),
		},
	}
}

// ABCI Evidence includes information from the past that's not included in the evidence itself
// so Evidence types stays compact.
func (tm2pb) Evidence(ev Evidence, valSet *ValidatorSet, evTime time.Time) abci.Evidence {
	_, val := valSet.GetByAddress(ev.Address())
	if val == nil {
		// should already have checked this
		panic(val)
	}

	// set type
	var evType string
	switch ev.(type) {
	case *DuplicateVoteEvidence:
		evType = ABCIEvidenceTypeDuplicateVote
	case *MockGoodEvidence, MockGoodEvidence:
		evType = ABCIEvidenceTypeMockGood
	default:
		panic(fmt.Sprintf("Unknown evidence type: %v %v", ev, reflect.TypeOf(ev)))
	}

	return abci.Evidence{
		Type:             evType,
		Validator:        TM2PB.Validator(val),
		Height:           ev.Height(),
		Time:             evTime.Unix(),
		TotalVotingPower: valSet.TotalVotingPower(),
	}
}

func (tm2pb) ValidatorFromPubKeyAndPower(pubkey crypto.PubKey, power int64) abci.Validator {
	pubkeyABCI := TM2PB.PubKey(pubkey)
	return abci.Validator{
		Address: pubkey.Address(),
		PubKey:  pubkeyABCI,
		Power:   power,
	}
}

//----------------------------------------------------------------------------

// PB2TM is used for converting protobuf ABCI to Tendermint ABCI.
// UNSTABLE
var PB2TM = pb2tm{}

type pb2tm struct{}

func (pb2tm) PubKey(pubKey abci.PubKey) (crypto.PubKey, error) {
	// TODO: define these in go-crypto and use them
	sizeEd := 32
	sizeSecp := 33
	switch pubKey.Type {
	case ABCIPubKeyTypeEd25519:
		if len(pubKey.Data) != sizeEd {
			return nil, fmt.Errorf("Invalid size for PubKeyEd25519. Got %d, expected %d", len(pubKey.Data), sizeEd)
		}
		var pk crypto.PubKeyEd25519
		copy(pk[:], pubKey.Data)
		return pk, nil
	case ABCIPubKeyTypeSecp256k1:
		if len(pubKey.Data) != sizeSecp {
			return nil, fmt.Errorf("Invalid size for PubKeyEd25519. Got %d, expected %d", len(pubKey.Data), sizeSecp)
		}
		var pk crypto.PubKeySecp256k1
		copy(pk[:], pubKey.Data)
		return pk, nil
	default:
		return nil, fmt.Errorf("Unknown pubkey type %v", pubKey.Type)
	}
}
<|endoftext|>


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

// Command ql is a utility to explore a database, prototype a schema or test
// drive a query, etc.
//
// Installation:
//
//	$ go get github.com/cznic/ql/ql
//
// Usage:
//
//	ql [-db name] [-schema regexp] [-tables regexp] [-fld] statement_list
//
// Options:
//
//	-db name	Name of the database to use. Defaults to "ql.db".
//			If the DB file does not exists it is created automatically.
//
//	-schema re	If re != "" show the CREATE statements of matching tables and exit.
//
//	-tables re	If re != "" show the matching table names and exit.
//
//	-fld		First row of a query result set will show field names.
//
//	statement_list	QL statements to execute.
//			If no non flag arguments are present, ql reads from stdin.
//			The list is wrapped into an automatic transaction.
//
//	-t		Report and measure time to execute, including creating/opening and closing the DB.
//
// Example:
//
//	$ ql 'create table t (i int, s string)'
//	$ ql << EOF
//	> insert into t values
//	> (1, "a"),
//	> (2, "b"),
//	> (3, "c"),
//	> EOF
//	$ ql 'select * from t'
//	3, "c"
//	2, "b"
//	1, "a"
//	$ ql -fld 'select * from t where i != 2 order by s'
//	"i", "s"
//	1, "a"
//	3, "c"
//	$
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"runtime"
	"sort"
	"strings"
	"time"

	"github.com/cznic/ql"
)

func str(data []interface{}) string {
	a := make([]string, len(data))
	for i, v := range data {
		switch x := v.(type) {
		case string:
			a[i] = fmt.Sprintf("%q", x)
		default:
			a[i] = fmt.Sprint(x)
		}
	}
	return strings.Join(a, ", ")
}

func main() {
	if err := do(); err != nil {
		log.Fatal(err)
	}
}

type config struct {
	db          string
	flds        bool
	schema      string
	tables      string
	time        bool
	help        bool
	interactive bool
}

func (c *config) parse() {
	db := flag.String("db", "ql.db", "The DB file to open. It'll be created if missing.")
	flds := flag.Bool("fld", false, "Show recordset's field names.")
	schema := flag.String("schema", "", "If non empty, show the CREATE statements of matching tables and exit.")
	tables := flag.String("tables", "", "If non empty, list matching table names and exit.")
	time := flag.Bool("t", false, "Measure and report time to execute the statement(s) including DB create/open/close.")
	help := flag.Bool("h", false, "Shows this help text.")
	interactive := flag.Bool("i", false, "runs in interactive mode")
	flag.Parse()
	c.flds = *flds
	c.db = *db
	c.schema = *schema
	c.tables = *tables
	c.time = *time
	c.help = *help
	c.interactive = *interactive
}

func do() (err error) {
	cfg := &config{}
	cfg.parse()
	if cfg.help || flag.NArg() == 0 && !cfg.interactive {
		flag.PrintDefaults()
		return nil
	}
	db, err := ql.OpenFile(cfg.db, &ql.Options{CanCreate: true})
	if err != nil {
		return err
	}

	defer func() {
		ec := db.Close()
		switch {
		case ec != nil && err != nil:
			log.Println(ec)
		case ec != nil:
			err = ec
		}
	}()
	r := bufio.NewReader(os.Stdin)
	o := bufio.NewWriter(os.Stdout)
	if cfg.interactive {
		for {
			o.WriteString("ql> ")
			o.Flush()
			src, err := readSrc(cfg.interactive, r)
			if err != nil {
				return err
			}
			err = run(cfg, o, src, db)
			if err != nil {
				fmt.Fprintln(o, err)
				o.Flush()
			}
		}
		return nil
	}
	src, err := readSrc(cfg.interactive, r)
	if err != nil {
		return err
	}
	return run(cfg, o, src, db)
}

func readSrc(i bool, in *bufio.Reader) (string, error) {
	if i {
		return in.ReadString('\n')
	}
	var src string
	switch n := flag.NArg(); n {
	case 0:
		b, err := ioutil.ReadAll(in)
		if err != nil {
			return "", err
		}

		src = string(b)
	default:
		a := make([]string, n)
		for i := range a {
			a[i] = flag.Arg(i)
		}
		src = strings.Join(a, " ")
	}
	return src, nil
}

func run(cfg *config, o *bufio.Writer, src string, db *ql.DB) (err error) {
	defer o.Flush()
	if cfg.interactive {
		src = strings.TrimSpace(src)
		if strings.HasPrefix(src, "\\") {
			switch src {
			case "\\clear":
				switch runtime.GOOS {
				case "darwin", "linux":
					fmt.Fprintln(o, "\033[H\033[2J")
				default:
					fmt.Fprintln(o, "clear not supported in this system")
				}
				return nil
			case "\\q", "\\exit":
				// we make sure to close the database before exiting
				db.Close()
				os.Exit(1)
			}
		}

	}

	t0 := time.Now()
	if cfg.time {
		defer func() {
			fmt.Fprintf(os.Stderr, "%s\n", time.Since(t0))
		}()
	}
	if pat := cfg.schema; pat != "" {
		re, err := regexp.Compile(pat)
		if err != nil {
			return err
		}

		nfo, err := db.Info()
		if err != nil {
			return err
		}

		r := []string{}
		for _, ti := range nfo.Tables {
			if !re.MatchString(ti.Name) {
				continue
			}

			a := []string{}
			for _, ci := range ti.Columns {
				a = append(a, fmt.Sprintf("%s %s", ci.Name, ci.Type))
			}
			r = append(r, fmt.Sprintf("CREATE TABLE %s (%s);", ti.Name, strings.Join(a, ", ")))
		}
		sort.Strings(r)
		if len(r) != 0 {
			fmt.Fprintln(o, strings.Join(r, "\n"))
		}
		return nil
	}

	if pat := cfg.tables; pat != "" {
		re, err := regexp.Compile(pat)
		if err != nil {
			return err
		}

		nfo, err := db.Info()
		if err != nil {
			return err
		}

		r := []string{}
		for _, ti := range nfo.Tables {
			if !re.MatchString(ti.Name) {
				continue
			}

			r = append(r, ti.Name)
		}
		sort.Strings(r)
		if len(r) != 0 {
			fmt.Fprintln(o, strings.Join(r, "\n"))
		}
		return nil
	}

	src = "BEGIN TRANSACTION; " + src + "; COMMIT;"
	l, err := ql.Compile(src)
	if err != nil {
		log.Println(src)
		return err
	}

	rs, i, err := db.Execute(ql.NewRWCtx(), l)
	if err != nil {
		a := strings.Split(strings.TrimSpace(fmt.Sprint(l)), "\n")
		return fmt.Errorf("%v: %s", err, a[i])
	}

	if len(rs) == 0 {
		return
	}

	switch {
	case l.IsExplainStmt():
		return rs[len(rs)-1].Do(cfg.flds, func(data []interface{}) (bool, error) {
			fmt.Fprintln(o, data[0])
			return true, nil
		})
	default:
		return rs[len(rs)-1].Do(cfg.flds, func(data []interface{}) (bool, error) {
			fmt.Fprintln(o, str(data))
			return true, nil
		})
	}
}
<commit_msg>support sqlite command syntax<commit_after>// Copyright 2014 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Command ql is a utility to explore a database, prototype a schema or test
// drive a query, etc.
//
// Installation:
//
//	$ go get github.com/cznic/ql/ql
//
// Usage:
//
//	ql [-db name] [-schema regexp] [-tables regexp] [-fld] statement_list
//
// Options:
//
//	-db name	Name of the database to use. Defaults to "ql.db".
//			If the DB file does not exists it is created automatically.
//
//	-schema re	If re != "" show the CREATE statements of matching tables and exit.
//
//	-tables re	If re != "" show the matching table names and exit.
//
//	-fld		First row of a query result set will show field names.
//
//	statement_list	QL statements to execute.
//			If no non flag arguments are present, ql reads from stdin.
//			The list is wrapped into an automatic transaction.
//
//	-t		Report and measure time to execute, including creating/opening and closing the DB.
//
// Example:
//
//	$ ql 'create table t (i int, s string)'
//	$ ql << EOF
//	> insert into t values
//	> (1, "a"),
//	> (2, "b"),
//	> (3, "c"),
//	> EOF
//	$ ql 'select * from t'
//	3, "c"
//	2, "b"
//	1, "a"
//	$ ql -fld 'select * from t where i != 2 order by s'
//	"i", "s"
//	1, "a"
//	3, "c"
//	$
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"runtime"
	"sort"
	"strings"
	"time"

	"github.com/cznic/ql"
)

func str(data []interface{}) string {
	a := make([]string, len(data))
	for i, v := range data {
		switch x := v.(type) {
		case string:
			a[i] = fmt.Sprintf("%q", x)
		default:
			a[i] = fmt.Sprint(x)
		}
	}
	return strings.Join(a, ", ")
}

func main() {
	if err := do(); err != nil {
		log.Fatal(err)
	}
}

type config struct {
	db          string
	flds        bool
	schema      string
	tables      string
	time        bool
	help        bool
	interactive bool
}

func (c *config) parse() {
	db := flag.String("db", "ql.db", "The DB file to open. It'll be created if missing.")
	flds := flag.Bool("fld", false, "Show recordset's field names.")
	schema := flag.String("schema", "", "If non empty, show the CREATE statements of matching tables and exit.")
	tables := flag.String("tables", "", "If non empty, list matching table names and exit.")
	time := flag.Bool("t", false, "Measure and report time to execute the statement(s) including DB create/open/close.")
	help := flag.Bool("h", false, "Shows this help text.")
	interactive := flag.Bool("i", false, "runs in interactive mode")
	flag.Parse()
	c.flds = *flds
	c.db = *db
	c.schema = *schema
	c.tables = *tables
	c.time = *time
	c.help = *help
	c.interactive = *interactive
}

func do() (err error) {
	cfg := &config{}
	cfg.parse()
	if cfg.help || flag.NArg() == 0 && !cfg.interactive {
		flag.PrintDefaults()
		return nil
	}
	db, err := ql.OpenFile(cfg.db, &ql.Options{CanCreate: true})
	if err != nil {
		return err
	}

	defer func() {
		ec := db.Close()
		switch {
		case ec != nil && err != nil:
			log.Println(ec)
		case ec != nil:
			err = ec
		}
	}()
	r := bufio.NewReader(os.Stdin)
	o := bufio.NewWriter(os.Stdout)
	if cfg.interactive {
		for {
			o.WriteString("ql> ")
			o.Flush()
			src, err := readSrc(cfg.interactive, r)
			if err != nil {
				return err
			}
			err = run(cfg, o, src, db)
			if err != nil {
				fmt.Fprintln(o, err)
				o.Flush()
			}
		}
		return nil
	}
	src, err := readSrc(cfg.interactive, r)
	if err != nil {
		return err
	}
	return run(cfg, o, src, db)
}

func readSrc(i bool, in *bufio.Reader) (string, error) {
	if i {
		return in.ReadString('\n')
	}
	var src string
	switch n := flag.NArg(); n {
	case 0:
		b, err := ioutil.ReadAll(in)
		if err != nil {
			return "", err
		}

		src = string(b)
	default:
		a := make([]string, n)
		for i := range a {
			a[i] = flag.Arg(i)
		}
		src = strings.Join(a, " ")
	}
	return src, nil
}

func run(cfg *config, o *bufio.Writer, src string, db *ql.DB) (err error) {
	defer o.Flush()
	if cfg.interactive {
		src = strings.TrimSpace(src)
		if strings.HasPrefix(src, "\\") ||
			strings.HasPrefix(src, ".") {
			switch src {
			case "\\clear", ".clear":
				switch runtime.GOOS {
				case "darwin", "linux":
					fmt.Fprintln(o, "\033[H\033[2J")
				default:
					fmt.Fprintln(o, "clear not supported in this system")
				}
				return nil
			case "\\q", "\\exit", ".q", ".exit":
				// we make sure to close the database before exiting
				db.Close()
				os.Exit(1)
			}
		}

	}

	t0 := time.Now()
	if cfg.time {
		defer func() {
			fmt.Fprintf(os.Stderr, "%s\n", time.Since(t0))
		}()
	}
	if pat := cfg.schema; pat != "" {
		re, err := regexp.Compile(pat)
		if err != nil {
			return err
		}

		nfo, err := db.Info()
		if err != nil {
			return err
		}

		r := []string{}
		for _, ti := range nfo.Tables {
			if !re.MatchString(ti.Name) {
				continue
			}

			a := []string{}
			for _, ci := range ti.Columns {
				a = append(a, fmt.Sprintf("%s %s", ci.Name, ci.Type))
			}
			r = append(r, fmt.Sprintf("CREATE TABLE %s (%s);", ti.Name, strings.Join(a, ", ")))
		}
		sort.Strings(r)
		if len(r) != 0 {
			fmt.Fprintln(o, strings.Join(r, "\n"))
		}
		return nil
	}

	if pat := cfg.tables; pat != "" {
		re, err := regexp.Compile(pat)
		if err != nil {
			return err
		}

		nfo, err := db.Info()
		if err != nil {
			return err
		}

		r := []string{}
		for _, ti := range nfo.Tables {
			if !re.MatchString(ti.Name) {
				continue
			}

			r = append(r, ti.Name)
		}
		sort.Strings(r)
		if len(r) != 0 {
			fmt.Fprintln(o, strings.Join(r, "\n"))
		}
		return nil
	}

	src = "BEGIN TRANSACTION; " + src + "; COMMIT;"
	l, err := ql.Compile(src)
	if err != nil {
		log.Println(src)
		return err
	}

	rs, i, err := db.Execute(ql.NewRWCtx(), l)
	if err != nil {
		a := strings.Split(strings.TrimSpace(fmt.Sprint(l)), "\n")
		return fmt.Errorf("%v: %s", err, a[i])
	}

	if len(rs) == 0 {
		return
	}

	switch {
	case l.IsExplainStmt():
		return rs[len(rs)-1].Do(cfg.flds, func(data []interface{}) (bool, error) {
			fmt.Fprintln(o, data[0])
			return true, nil
		})
	default:
		return rs[len(rs)-1].Do(cfg.flds, func(data []interface{}) (bool, error) {
			fmt.Fprintln(o, str(data))
			return true, nil
		})
	}
}
<|endoftext|>


<commit_before>package ltxref

import (
	"sort"
	"strings"

	"github.com/renstrom/fuzzysearch/fuzzy"
)

func (l *Ltxref) AddCommand(commandname string) (*Command, error) {
	cmd := &Command{}
	cmd.Name = commandname
	l.Commands = append(l.Commands, cmd)
	return cmd, nil
}

// packagename may be empty for the kernel commands
func (l *Ltxref) GetCommandFromPackage(commandname string, packagename string) *Command {
	var cmdlist []*Command
	// Needs better implementation!
	if packagename != "" {
		for _, v := range l.Packages {
			if v.Name == packagename {
				cmdlist = v.Commands
				break
			}
		}
	} else {
		cmdlist = l.Commands
	}

	for _, v := range cmdlist {
		if v.Name == commandname {
			return v
		}
	}
	return nil
}

func (l *Ltxref) GetDocumentclass(name string) *Documentclass {
	for _, class := range l.Documentclasses {
		if class.Name == name {
			return &class
		}
	}
	return nil
}

func (l *Ltxref) GetEnvironmentWithName(name string) *Environment {
	for _, env := range l.Environments {
		if env.Name == name {
			return &env
		}
	}
	return nil
}

func (l *Ltxref) GetPackageWithName(name string) *Package {
	for _, pkg := range l.Packages {
		if pkg.Name == name {
			return &pkg
		}
	}
	return nil
}

// Returns all tags in alphabetical order.
func (l *Ltxref) Tags() []string {
	// Needs better implementation!
	tags := make(map[string]bool)
	for _, command := range l.Commands {
		for _, label := range command.Label {
			tags[label] = true
		}
	}
	for _, v := range l.Environments {
		for _, label := range v.Label {
			tags[label] = true
		}
	}

	for _, v := range l.Packages {
		for _, label := range v.Label {
			tags[label] = true
		}
		for _, cmd := range v.Commands {
			for _, label := range cmd.Label {
				tags[label] = true
			}

		}
	}
	for _, v := range l.Documentclasses {
		for _, label := range v.Label {
			tags[label] = true
		}
	}

	mk := make([]string, len(tags))
	i := 0
	for k, _ := range tags {
		mk[i] = k
		i++
	}
	sort.Strings(mk)
	return mk
}

// Case insensitive fuzzy match.
func (l *Ltxref) FilterCommands(like string, tag string) []*Command {
	if like == "" && tag == "" {
		return l.Commands
	} else {
		like = strings.ToLower(like)
		tag = strings.ToLower(tag)
	}
	var commandsThatMatch []*Command
	for _, command := range l.Commands {
		if fuzzy.Match(like, command.Name) && (tag == "" || hasTag(command.Label, tag)) {
			commandsThatMatch = append(commandsThatMatch, command)
		}
	}
	return commandsThatMatch
}

// Case insensitive fuzzy match.
func (l *Ltxref) FilterEnvironments(like string, tag string) []Environment {
	if like == "" && tag == "" {
		return l.Environments
	} else {
		like = strings.ToLower(like)
		tag = strings.ToLower(tag)
	}
	var itemsThatMatch []Environment
	for _, item := range l.Environments {
		if fuzzy.Match(like, item.Name) && (tag == "" || hasTag(item.Label, tag)) {
			itemsThatMatch = append(itemsThatMatch, item)
		}
	}
	return itemsThatMatch
}

// Case insensitive fuzzy match.
func (l *Ltxref) FilterDocumentclasses(like string, tag string) []Documentclass {
	if like == "" && tag == "" {
		return l.Documentclasses
	} else {
		like = strings.ToLower(like)
		tag = strings.ToLower(tag)
	}
	var itemsThatMatch []Documentclass
	for _, item := range l.Documentclasses {
		if fuzzy.Match(like, item.Name) && (tag == "" || hasTag(item.Label, tag)) {
			itemsThatMatch = append(itemsThatMatch, item)
		}
	}
	return itemsThatMatch
}

// Case insensitive fuzzy match.
func (l *Ltxref) FilterPackages(like string, tag string) []Package {
	if like == "" && tag == "" {
		return l.Packages
	} else {
		like = strings.ToLower(like)
		tag = strings.ToLower(tag)
	}
	var itemsThatMatch []Package
	for _, item := range l.Packages {
		if fuzzy.Match(like, item.Name) && (tag == "" || hasTag(item.Label, tag)) {
			itemsThatMatch = append(itemsThatMatch, item)
		}
	}
	return itemsThatMatch
}

// Return true if command c has the given label (tag)
func hasTag(labels []string, label string) bool {
	for _, v := range labels {
		if v == label {
			return true
		}
	}
	return false
}
<commit_msg>Fix initialization of Command<commit_after>package ltxref

import (
	"sort"
	"strings"

	"github.com/renstrom/fuzzysearch/fuzzy"
)

func (l *Ltxref) AddCommand(commandname string) (*Command, error) {
	cmd := NewCommand()
	cmd.Name = commandname
	l.Commands = append(l.Commands, cmd)
	return cmd, nil
}

// packagename may be empty for the kernel commands
func (l *Ltxref) GetCommandFromPackage(commandname string, packagename string) *Command {
	var cmdlist []*Command
	// Needs better implementation!
	if packagename != "" {
		for _, v := range l.Packages {
			if v.Name == packagename {
				cmdlist = v.Commands
				break
			}
		}
	} else {
		cmdlist = l.Commands
	}

	for _, v := range cmdlist {
		if v.Name == commandname {
			return v
		}
	}
	return nil
}

func (l *Ltxref) GetDocumentclass(name string) *Documentclass {
	for _, class := range l.Documentclasses {
		if class.Name == name {
			return &class
		}
	}
	return nil
}

func (l *Ltxref) GetEnvironmentWithName(name string) *Environment {
	for _, env := range l.Environments {
		if env.Name == name {
			return &env
		}
	}
	return nil
}

func (l *Ltxref) GetPackageWithName(name string) *Package {
	for _, pkg := range l.Packages {
		if pkg.Name == name {
			return &pkg
		}
	}
	return nil
}

// Returns all tags in alphabetical order.
func (l *Ltxref) Tags() []string {
	// Needs better implementation!
	tags := make(map[string]bool)
	for _, command := range l.Commands {
		for _, label := range command.Label {
			tags[label] = true
		}
	}
	for _, v := range l.Environments {
		for _, label := range v.Label {
			tags[label] = true
		}
	}

	for _, v := range l.Packages {
		for _, label := range v.Label {
			tags[label] = true
		}
		for _, cmd := range v.Commands {
			for _, label := range cmd.Label {
				tags[label] = true
			}

		}
	}
	for _, v := range l.Documentclasses {
		for _, label := range v.Label {
			tags[label] = true
		}
	}

	mk := make([]string, len(tags))
	i := 0
	for k, _ := range tags {
		mk[i] = k
		i++
	}
	sort.Strings(mk)
	return mk
}

// Case insensitive fuzzy match.
func (l *Ltxref) FilterCommands(like string, tag string) []*Command {
	if like == "" && tag == "" {
		return l.Commands
	} else {
		like = strings.ToLower(like)
		tag = strings.ToLower(tag)
	}
	var commandsThatMatch []*Command
	for _, command := range l.Commands {
		if fuzzy.Match(like, command.Name) && (tag == "" || hasTag(command.Label, tag)) {
			commandsThatMatch = append(commandsThatMatch, command)
		}
	}
	return commandsThatMatch
}

// Case insensitive fuzzy match.
func (l *Ltxref) FilterEnvironments(like string, tag string) []Environment {
	if like == "" && tag == "" {
		return l.Environments
	} else {
		like = strings.ToLower(like)
		tag = strings.ToLower(tag)
	}
	var itemsThatMatch []Environment
	for _, item := range l.Environments {
		if fuzzy.Match(like, item.Name) && (tag == "" || hasTag(item.Label, tag)) {
			itemsThatMatch = append(itemsThatMatch, item)
		}
	}
	return itemsThatMatch
}

// Case insensitive fuzzy match.
func (l *Ltxref) FilterDocumentclasses(like string, tag string) []Documentclass {
	if like == "" && tag == "" {
		return l.Documentclasses
	} else {
		like = strings.ToLower(like)
		tag = strings.ToLower(tag)
	}
	var itemsThatMatch []Documentclass
	for _, item := range l.Documentclasses {
		if fuzzy.Match(like, item.Name) && (tag == "" || hasTag(item.Label, tag)) {
			itemsThatMatch = append(itemsThatMatch, item)
		}
	}
	return itemsThatMatch
}

// Case insensitive fuzzy match.
func (l *Ltxref) FilterPackages(like string, tag string) []Package {
	if like == "" && tag == "" {
		return l.Packages
	} else {
		like = strings.ToLower(like)
		tag = strings.ToLower(tag)
	}
	var itemsThatMatch []Package
	for _, item := range l.Packages {
		if fuzzy.Match(like, item.Name) && (tag == "" || hasTag(item.Label, tag)) {
			itemsThatMatch = append(itemsThatMatch, item)
		}
	}
	return itemsThatMatch
}

// Return true if command c has the given label (tag)
func hasTag(labels []string, label string) bool {
	for _, v := range labels {
		if v == label {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strconv"
	"strings"
)

const (
	EraseLine  string = "\033[2K"
	Move              = "\033[%d;%dH"
	Invert            = "\033[7m"
	Reset             = "\033[0m"
	HideCursor        = "\033[?25l"
	ShowCursor        = "\033[?25h"
)

type Screen struct {
	Height int
	Width  int
	tty    *TTY
}

func NewScreen(tty *TTY) *Screen {
	height, width := parseSize(tty.Stty("size"))

	return &Screen{
		tty:    tty,
		Height: height,
		Width:  width,
	}
}

func parseSize(size string) (int, int) {
	ssize := strings.Fields(size)
	height, _ := strconv.Atoi(ssize[0])
	width, _ := strconv.Atoi(ssize[1])
	return height, width
}

func (s *Screen) ConfigScreen() {
	s.tty.Stty("-echo", "-icanon")
}

func (s *Screen) MakeRoom(rows int) {
	for i := 0; i < rows; i++ {
		s.tty.Puts("")
	}
}

func (s *Screen) HideCursor() {
	s.tty.Write(HideCursor)
}

func (s *Screen) ShowCursor() {
	s.tty.Write(ShowCursor)
}

func (s *Screen) MoveTo(x, y int) {
	s.tty.Write(fmt.Sprintf(Move, x+1, y+1))
}

func (s *Screen) MoveToRow(x int) {
	s.MoveTo(x, 0)
}

func (s *Screen) Draw(view *View) {
	s.HideCursor()
	promptRow := s.Height - view.Height
	for i, row := range ttyView(view) {
		s.MoveToRow(promptRow + i)
		s.tty.Write(ClearLine)

	start_row := s.Height - view.Height - 1

		s.tty.Write(row)
	}
	// XXX: 3 magic number
	s.ShowCursor()
	s.MoveTo(start_row, len(view.Query)+2)
}

func ansiInverted(s string) string {
	return Invert + s + Reset
}

func ttyView(view *View) []string {
	rows := make([]string, view.Height+1)
	rows[0] = "> " + view.Query
	copy(rows[1:], view.Rows)
	if len(view.Rows) > 0 {
		rows[view.Index()+1] = ansiInverted(view.Selected())
	}
	return rows
}
<commit_msg>Truncate lines longer than the terminal<commit_after>package main

import (
	"fmt"
	"strconv"
	"strings"
)

const (
	EraseLine  string = "\033[2K"
	Move              = "\033[%d;%dH"
	Invert            = "\033[7m"
	Reset             = "\033[0m"
	HideCursor        = "\033[?25l"
	ShowCursor        = "\033[?25h"
)

type Screen struct {
	Height int
	Width  int
	tty    *TTY
}

func NewScreen(tty *TTY) *Screen {
	height, width := parseSize(tty.Stty("size"))

	return &Screen{
		tty:    tty,
		Height: height,
		Width:  width,
	}
}

func parseSize(size string) (int, int) {
	ssize := strings.Fields(size)
	height, _ := strconv.Atoi(ssize[0])
	width, _ := strconv.Atoi(ssize[1])
	return height, width
}

func (s *Screen) ConfigScreen() {
	s.tty.Stty("-echo", "-icanon")
}

func (s *Screen) MakeRoom(rows int) {
	for i := 0; i < rows; i++ {
		s.tty.Puts("")
	}
}

func (s *Screen) HideCursor() {
	s.tty.Write(HideCursor)
}

func (s *Screen) ShowCursor() {
	s.tty.Write(ShowCursor)
}

func (s *Screen) MoveTo(x, y int) {
	s.tty.Write(fmt.Sprintf(Move, x+1, y+1))
}

func (s *Screen) MoveToRow(x int) {
	s.MoveTo(x, 0)
}

func (s *Screen) Draw(view *View) {
	s.HideCursor()
	defer s.ShowCursor()

	start_row := s.Height - view.Height - 1

	for i, row := range ttyView(view, s.Width) {
		s.MoveToRow(start_row + i)
		s.tty.Write(EraseLine)
		s.tty.Write(row)
	}

	// XXX: 3 magic number
	s.MoveTo(start_row, len(view.Query)+2)
}

func ansiInverted(s string) string {
	return Invert + s + Reset
}

func ttyView(view *View, width int) []string {
	rows := make([]string, view.Height+1)
	rows[0] = "> " + view.Query
	for i, row := range view.Rows {
		if len(row) > width {
			row = row[:width]
		}
		rows[i+1] = row
	}
	if len(view.Rows) > 0 {
		rows[view.Index()+1] = ansiInverted(view.Selected())
	}
	return rows
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
	"strings"
)

func init() {
	publicKeyFile := util.FileFlag{}
	commands = append(commands, cli.Command{
		Name:        "add",
		Usage:       "add SSH keys to a user / IPs to a server",
		UsageText:   "bytemark add key|ip",
		Description: "add SSH keys to a user or IPs to a server",
		Action:      cli.ShowSubcommandHelp,
		Subcommands: []cli.Command{{
			Name:        "key",
			Usage:       "add public SSH keys to a Bytemark user",
			UsageText:   "bytemark add key [--user <user>] [--public-key-file <filename>] <key>",
			Description: `Add the given public key to the given user (or the default user). This will allow them to use that key to access management IPs they have access to using that key. To remove a key, use the remove key command. --public-key-file will be ignored if a public key is specified in the arguments`,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "user",
					Usage: "Which user to add the key to. Defaults to the username you log in as.",
				},
				cli.GenericFlag{
					Name:  "public-key-file",
					Usage: "The public key file to add to the account",
					Value: &publicKeyFile,
				},
			},
			Action: With(AuthProvider, func(ctx *Context) (err error) {
				user := ctx.String("user")
				if user == "" {
					user = global.Config.GetIgnoreErr("user")
				}

				key := strings.TrimSpace(strings.Join(ctx.Args(), " "))
				if key == "" {
					key = publicKeyFile.Value
				}

				err = global.Client.AddUserAuthorizedKey(user, key)
				if err == nil {
					log.Log("Key added successfully")
				}
				return
			}),
		}, {
			Name:        "ips",
			Aliases:     []string{"ip"},
			Usage:       "add extra IP addresses to a server",
			UsageText:   "bytemark add ips [--ipv4 | --ipv6] [--ips <number>] <server name>",
			Description: `Add an extra IP to the given server. The IP will be chosen by the brain and output to standard out.`,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "ipv4",
					Usage: "Add an IPv4 address. This is the default",
				},
				cli.BoolFlag{
					Name:  "ipv6",
					Usage: "Add an IPv6 address.",
				},
				cli.IntFlag{
					Name:  "ips",
					Usage: "How many IPs to add (1 to 4) - defaults to one.",
				},
				cli.StringFlag{
					Name:  "reason",
					Usage: "Reason for adding the IP. If not set, will prompt.",
				},
			},
			Action: With(VirtualMachineNameProvider, AuthProvider, func(c *Context) error {
				addrs := c.Int("ips")
				if addrs < 1 {
					addrs = 1
				}
				family := "ipv4"
				if c.Bool("ipv6") {
					if c.Bool("ipv4") {
						return c.Help("--ipv4 cannot be specified at the same time as --ipv6")
					}
					family = "ipv6"
				}
				reason := c.String("reason")
				if reason == "" {
					if addrs == 1 {
						reason = util.Prompt("Enter the purpose for this extra IP: ")
					} else {
						reason = util.Prompt("Enter the purpose for these extra IPs: ")
					}
				}
				ipcr := brain.IPCreateRequest{
					Addresses:  addrs,
					Family:     family,
					Reason:     reason,
					Contiguous: c.Bool("contiguous"),
				}
				ips, err := global.Client.AddIP(c.VirtualMachineName, &ipcr)
				if err != nil {
					return err
				}
				log.Log("IPs addded:")
				log.Output(ips.String(), "\r\n")
				return nil
			}),
		}},
	})
}
<commit_msg>Return if no key specified<commit_after>package main

import (
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
	"strings"
)

func init() {
	publicKeyFile := util.FileFlag{}
	commands = append(commands, cli.Command{
		Name:        "add",
		Usage:       "add SSH keys to a user / IPs to a server",
		UsageText:   "bytemark add key|ip",
		Description: "add SSH keys to a user or IPs to a server",
		Action:      cli.ShowSubcommandHelp,
		Subcommands: []cli.Command{{
			Name:        "key",
			Usage:       "add public SSH keys to a Bytemark user",
			UsageText:   "bytemark add key [--user <user>] [--public-key-file <filename>] <key>",
			Description: `Add the given public key to the given user (or the default user). This will allow them to use that key to access management IPs they have access to using that key. To remove a key, use the remove key command. --public-key-file will be ignored if a public key is specified in the arguments`,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "user",
					Usage: "Which user to add the key to. Defaults to the username you log in as.",
				},
				cli.GenericFlag{
					Name:  "public-key-file",
					Usage: "The public key file to add to the account",
					Value: &publicKeyFile,
				},
			},
			Action: With(AuthProvider, func(ctx *Context) (err error) {
				user := ctx.String("user")
				if user == "" {
					user = global.Config.GetIgnoreErr("user")
				}

				key := strings.TrimSpace(strings.Join(ctx.Args(), " "))
				if key == "" {
					if publicKeyFile.Value == "" {
						log.Log("Please specify a key")
						return
					}
					key = publicKeyFile.Value
				}

				err = global.Client.AddUserAuthorizedKey(user, key)
				if err == nil {
					log.Log("Key added successfully")
				}
				return
			}),
		}, {
			Name:        "ips",
			Aliases:     []string{"ip"},
			Usage:       "add extra IP addresses to a server",
			UsageText:   "bytemark add ips [--ipv4 | --ipv6] [--ips <number>] <server name>",
			Description: `Add an extra IP to the given server. The IP will be chosen by the brain and output to standard out.`,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "ipv4",
					Usage: "Add an IPv4 address. This is the default",
				},
				cli.BoolFlag{
					Name:  "ipv6",
					Usage: "Add an IPv6 address.",
				},
				cli.IntFlag{
					Name:  "ips",
					Usage: "How many IPs to add (1 to 4) - defaults to one.",
				},
				cli.StringFlag{
					Name:  "reason",
					Usage: "Reason for adding the IP. If not set, will prompt.",
				},
			},
			Action: With(VirtualMachineNameProvider, AuthProvider, func(c *Context) error {
				addrs := c.Int("ips")
				if addrs < 1 {
					addrs = 1
				}
				family := "ipv4"
				if c.Bool("ipv6") {
					if c.Bool("ipv4") {
						return c.Help("--ipv4 cannot be specified at the same time as --ipv6")
					}
					family = "ipv6"
				}
				reason := c.String("reason")
				if reason == "" {
					if addrs == 1 {
						reason = util.Prompt("Enter the purpose for this extra IP: ")
					} else {
						reason = util.Prompt("Enter the purpose for these extra IPs: ")
					}
				}
				ipcr := brain.IPCreateRequest{
					Addresses:  addrs,
					Family:     family,
					Reason:     reason,
					Contiguous: c.Bool("contiguous"),
				}
				ips, err := global.Client.AddIP(c.VirtualMachineName, &ipcr)
				if err != nil {
					return err
				}
				log.Log("IPs addded:")
				log.Output(ips.String(), "\r\n")
				return nil
			}),
		}},
	})
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"

	"bazil.org/fuse"
	"git.timschuster.info/rls.moe/catgi/backend"
	"github.com/tscs37/fuse/fs"
)

func main() {
}

type CatgiFS struct {
	index backend.KVBackend
	data  backend.ContentBackend
}

func (c *CatgiFS) Ctx() context.Context {
	return context.Background()
}

func (c *CatgiFS) Root() (fs.Node, error) {
	return &CatgiFSNode{
		fs: c,
	}, nil
}

type CatgiFSNode struct {
	fs    *CatgiFS
	inode uint64
}

func (c *CatgiFSNode) Attr(ctx context.Context, attr *fuse.Attr) error {
	attrs, err := c.fs.index.Get(ctx, fmt.Sprintf("attr:/%d", c.inode))
	if v, ok := attrs.(fuse.Attr); ok {
		attr.Size = v.Size
		attr.Atime = v.Atime
		attr.Crtime = v.Crtime
		attr.Ctime = v.Ctime
		attr.Flags = v.Flags
	}
	return err
}
<commit_msg>Removed Catgifs due to build errors<commit_after><|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/dustin/go-humanize"
	"github.com/olekukonko/tablewriter"
	"github.com/umayr/dilawar"
	"github.com/urfave/cli"
)

func main() {

	app := cli.NewApp()
	app.Name = "dilawar"
	app.Usage = "tiny finance manager"
	app.Version = "0.1.0"

	app.Action = func(c *cli.Context) error {
		b, err := dilawar.Balance()
		if err != nil {
			return err
		}

		fmt.Printf("%d\n", b)
		return nil
	}

	app.Commands = []cli.Command{
		{
			Name:  "debit",
			Usage: "adds amount in debit",
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "amount, a",
					Usage: "amount to be added",
					Value: 0,
				},
				cli.StringFlag{
					Name:  "message, m",
					Usage: "message for the transaction",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Int("amount") <= 0 {
					return cli.NewExitError("provided amount is invalid", 1)
				}

				return dilawar.Debit(c.Int("amount"), c.String("message"))
			},
		},
		{
			Name:  "credit",
			Usage: "adds amount in credit",
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "amount, a",
					Usage: "amount to be added",
					Value: 0,
				},
				cli.StringFlag{
					Name:  "message, m",
					Usage: "message for the transaction",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Int("amount") <= 0 {
					return cli.NewExitError("provided amount is invalid", 1)
				}

				return dilawar.Credit(c.Int("amount"), c.String("message"))
			},
		},
		{
			Name:  "history",
			Usage: "shows all history",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "csv",
					Usage: "outputs as comma seperate values",
				},
			},
			Action: func(c *cli.Context) error {
				items, err := dilawar.History()
				if err != nil {
					return cli.NewExitError(fmt.Sprintf("error: %s", err.Error()), 127)
				}

				data := [][]string{}

				for _, v := range items {
					var (
						id   string
						tm   string
						desc string
						abbr string
						amt  string
					)

					if v.Type == dilawar.TypeCredit {
						abbr = "CR"
					} else if v.Type == dilawar.TypeDebit {
						abbr = "DR"
					}

					id = strconv.Itoa(int(v.ID))
					tm = humanize.Time(v.Time)
					if v.Description == "" {
						desc = "N/A"
					} else {
						desc = v.Description
					}
					amt = fmt.Sprintf("%s %s", humanize.Comma(int64(v.Amount)), "PKR")

					data = append(data, []string{id, desc, tm, abbr, amt})
				}

				b, err := dilawar.Balance()
				if err != nil {
					return cli.NewExitError(fmt.Sprintf("error: %s", err.Error()), 127)
				}

				head := []string{"ID", "Description", "Time", "Type", "Amount"}
				foot := []string{"", "", "", "Total", humanize.Comma(int64(b))}

				if c.Bool("csv") {
					fmt.Println(strings.Join(head, ","))
					for _, line := range data {
						fmt.Println(strings.Join(line, ","))
					}
					fmt.Println(strings.Join(foot, ","))
					return nil
				}

				table := tablewriter.NewWriter(os.Stdout)
				table.SetHeader(head)
				table.SetFooter(foot)
				table.SetBorder(false)
				table.AppendBulk(data)
				table.Render()

				return nil
			},
		},
	}
	app.Run(os.Args)
}
<commit_msg>add PKR suffix<commit_after>package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/dustin/go-humanize"
	"github.com/olekukonko/tablewriter"
	"github.com/umayr/dilawar"
	"github.com/urfave/cli"
)

func main() {

	app := cli.NewApp()
	app.Name = "dilawar"
	app.Usage = "tiny finance manager"
	app.Version = "0.1.0"

	app.Action = func(c *cli.Context) error {
		b, err := dilawar.Balance()
		if err != nil {
			return err
		}

		fmt.Printf("%d PKR\n", b)
		return nil
	}

	app.Commands = []cli.Command{
		{
			Name:  "debit",
			Usage: "adds amount in debit",
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "amount, a",
					Usage: "amount to be added",
					Value: 0,
				},
				cli.StringFlag{
					Name:  "message, m",
					Usage: "message for the transaction",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Int("amount") <= 0 {
					return cli.NewExitError("provided amount is invalid", 1)
				}

				return dilawar.Debit(c.Int("amount"), c.String("message"))
			},
		},
		{
			Name:  "credit",
			Usage: "adds amount in credit",
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "amount, a",
					Usage: "amount to be added",
					Value: 0,
				},
				cli.StringFlag{
					Name:  "message, m",
					Usage: "message for the transaction",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Int("amount") <= 0 {
					return cli.NewExitError("provided amount is invalid", 1)
				}

				return dilawar.Credit(c.Int("amount"), c.String("message"))
			},
		},
		{
			Name:  "history",
			Usage: "shows all history",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "csv",
					Usage: "outputs as comma seperate values",
				},
			},
			Action: func(c *cli.Context) error {
				items, err := dilawar.History()
				if err != nil {
					return cli.NewExitError(fmt.Sprintf("error: %s", err.Error()), 127)
				}

				data := [][]string{}

				for _, v := range items {
					var (
						id   string
						tm   string
						desc string
						abbr string
						amt  string
					)

					if v.Type == dilawar.TypeCredit {
						abbr = "CR"
					} else if v.Type == dilawar.TypeDebit {
						abbr = "DR"
					}

					id = strconv.Itoa(int(v.ID))
					tm = humanize.Time(v.Time)
					if v.Description == "" {
						desc = "N/A"
					} else {
						desc = v.Description
					}
					amt = fmt.Sprintf("%s %s", humanize.Comma(int64(v.Amount)), "PKR")

					data = append(data, []string{id, desc, tm, abbr, amt})
				}

				b, err := dilawar.Balance()
				if err != nil {
					return cli.NewExitError(fmt.Sprintf("error: %s", err.Error()), 127)
				}

				head := []string{"ID", "Description", "Time", "Type", "Amount"}
				foot := []string{"", "", "", "Total", fmt.Sprintf("%s PKR", humanize.Comma(int64(b)))}

				if c.Bool("csv") {
					fmt.Println(strings.Join(head, ","))
					for _, line := range data {
						fmt.Println(strings.Join(line, ","))
					}
					fmt.Println(strings.Join(foot, ","))
					return nil
				}

				table := tablewriter.NewWriter(os.Stdout)
				table.SetHeader(head)
				table.SetFooter(foot)
				table.SetBorder(false)
				table.AppendBulk(data)
				table.Render()

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