<commit_before>// Main file

package webhook

import (
    "appengine"
    "appengine/datastore"
    "appengine/user"
    "encoding/json"
    "fmt"
    "github.com/gorilla/mux"
    "html/template"
    "net/http"
    "services"
    "strings"
    "time"
)

var redirectTmpl = template.Must(
    template.ParseFiles("templates/callback.html"))

// Initialize Appengine.
// Only routes, that's it!
func init() {
    route := mux.NewRouter()
    route.HandleFunc("/", root)
    route.HandleFunc("/cb", callback)
    route.HandleFunc("/connect", connect)
    route.HandleFunc("/created.json", createdJson)
    route.HandleFunc("/delete/{handler}", deleteHandler)
    route.HandleFunc("/login", login)
    route.HandleFunc("/redirect", redirect)
    route.HandleFunc("/save", save)
    route.HandleFunc("/telegram/{telegramToken}", telegramWebhook)
    route.HandleFunc("/trello/{type}/{boardid}", trelloList)
    route.HandleFunc("/w{handler}", hooks)
    http.Handle("/", route)
}

// Return list of created webhooks (/created.json)
func createdJson(writer http.ResponseWriter, request *http.Request) {
    context := appengine.NewContext(request)
    appUser := user.Current(context)
    webhooks := getWebhooks(context, appUser.Email)
    list, _ := json.Marshal(webhooks)
    writer.Header().Set("Content-Type", "application/json")
    fmt.Fprintf(writer, string(list))
}

// Redirect use to get trello service approval. (/connect)
func login(writer http.ResponseWriter, request *http.Request) {
    http.Redirect(writer, request, "/", http.StatusFound)
}

// Root handler (/), show for to create new and list of created hooks.
func root(writer http.ResponseWriter, request *http.Request) {
    context := appengine.NewContext(request)
    appUser := user.Current(context)
    if appUser != nil {
        listTmpl := template.Must(
            template.ParseFiles("templates/base.html", "templates/list.html"))
        url, _ := user.LogoutURL(context, "/")
        data := struct {
            AccessToken string
            Logout      string
        }{getAccessToken(context, appUser.Email), url}
        if err := listTmpl.Execute(writer, data); err != nil {
            http.Error(writer, err.Error(), http.StatusInternalServerError)
        }
    } else {
        homeTmpl := template.Must(template.ParseFiles("templates/index.html"))
        homeTmpl.Execute(writer, nil)
    }
}

// Redirect use to get trello service approval. (/connect)
func connect(writer http.ResponseWriter, request *http.Request) {
    http.Redirect(writer, request, services.GetAuthorizeUrl(), http.StatusFound)
}

// Once approval from service is done, read the token, make post request
// to callback handler (/cb) to save token.
func redirect(writer http.ResponseWriter, request *http.Request) {
    if err := redirectTmpl.Execute(writer, nil); err != nil {
        http.Error(writer, err.Error(), http.StatusInternalServerError)
    }
}

// Callback with token in post payload.
func callback(writer http.ResponseWriter, request *http.Request) {
    context := appengine.NewContext(request)
    appUser := user.Current(context)
    accessToken := AccessTokens{
        Email:       appUser.Email,
        AccessToken: request.FormValue("token"),
    }
    key := datastore.NewIncompleteKey(
        context, "AccessTokens", accessTokenKey(context, appUser.Email))
    _, err := datastore.Put(context, key, &accessToken)
    if err != nil {
        http.Error(writer, err.Error(), http.StatusInternalServerError)
        return
    }
    http.Redirect(writer, request, "/", http.StatusFound)
}

// Get list of trello boards or lists.
func trelloList(writer http.ResponseWriter, request *http.Request) {
    vars := mux.Vars(request)
    context := appengine.NewContext(request)
    appUser := user.Current(context)
    writer.Header().Set("Content-Type", "application/json")
    accessToken := getAccessToken(context, appUser.Email)
    if vars["type"] == "lists" {
        fmt.Fprintf(
            writer, services.GetBoardLists(
                context, vars["boardid"], accessToken))
        return
    }
    fmt.Fprintf(writer, services.GetBoards(context, accessToken))
}

// Save new hook from web.
func save(writer http.ResponseWriter, request *http.Request) {
    context := appengine.NewContext(request)
    appUser := user.Current(context)
    response := Response{
        Success: true,
        Reason:  "",
    }
    handler := "w" + services.GetAlphaNumberic(7)
    webhook := Webhook{
        User:    appUser.Email,
        Handler: handler,
        Date:    time.Now(),
        Count:   0,
    }
    if request.FormValue("service") == "trello" {
        webhook.Type = "Trello"
        webhook.BoardId = request.FormValue("boardId")
        webhook.BoardName = request.FormValue("boardName")
        webhook.ListId = request.FormValue("listId")
        webhook.ListName = request.FormValue("listName")
        services.PushToTrello(
            context, webhook.ListId,
            getAccessToken(context, webhook.User), "You are connected!", "")
    } else if request.FormValue("service") == "telegram" {
        webhook.Type = "Telegram"
        webhook.TeleChatId, webhook.TeleChatName = services.GetChatIdFromCode(
            context, request.FormValue("teleCode"))
        if webhook.TeleChatId == 0 {
            response.Success = false
            response.Reason = "Invalid code."
        } else {
            services.SendTeleMessage(
                context, "You are connected!", webhook.TeleChatId)
        }
    } else if request.FormValue("service") == "pushover" {
        webhook.Type = "Pushover"
        webhook.POUserKey = request.FormValue("poUserkey")
        status := services.SendPushoverMessage(
            context, "You are connected!", webhook.POUserKey)
        if status == 0 {
            response.Success = false
            response.Reason = "Invalid key."
        }
    } else if request.FormValue("service") == "hipchat" {
        webhook.Type = "Hipchat"
        webhook.HCToken = request.FormValue("hcToken")
        webhook.HCRoomId = request.FormValue("hcRoomid")
        status := services.SendHipchatMessage(
            context, "You are connected!", webhook.HCRoomId,
            webhook.HCToken, "green")
        if !status {
            response.Success = false
            response.Reason = "Invalid room id or token."
        }
    }
    if response.Success {
        key := datastore.NewIncompleteKey(
            context, "Webhook", webhookKey(context, handler))
        _, err := datastore.Put(context, key, &webhook)
        if err != nil {
            context.Infof("%v", err.Error())
            return
        }
        response.Handler = handler
    }
    writer.Header().Set("Content-Type", "application/json")
    resp, _ := json.Marshal(response)
    fmt.Fprintf(writer, string(resp))
}

func deleteHandler(writer http.ResponseWriter, request *http.Request) {
    vars := mux.Vars(request)
    context := appengine.NewContext(request)
    appUser := user.Current(context)
    response := Response{
        Success: false,
        Reason:  "Not found",
    }
    webhook := getWebhookFromHandler(context, vars["handler"])
    if webhook != nil && webhook.User == appUser.Email {
        response.Success = true
        response.Reason = ""
        deleteWebhookFromHandler(context, vars["handler"])
    }
    writer.Header().Set("Content-Type", "application/json")
    resp, _ := json.Marshal(response)
    fmt.Fprintf(writer, string(resp))
}

// Telegram webhook
func telegramWebhook(writer http.ResponseWriter, request *http.Request) {
    vars := mux.Vars(request)
    context := appengine.NewContext(request)
    decoder := json.NewDecoder(request.Body)
    fmt.Fprintf(
        writer, services.Telegram(context, decoder, vars["telegramToken"]))
}

// Actual webhook handler, receive events and post it to connected services.
func hooks(writer http.ResponseWriter, request *http.Request) {
    vars := mux.Vars(request)
    handler := "w" + vars["handler"]
    context := appengine.NewContext(request)
    webhook := getWebhookFromHandler(context, handler)
    if webhook != nil {
        event, desc := services.GetEventData(request)
        context.Infof("%s: %s \n %s", webhook.Type, event, desc)
        if event != "" {
            if webhook.Type == "Trello" {
                services.PushToTrello(
                    context, webhook.ListId,
                    getAccessToken(context, webhook.User), event, desc)
            } else if webhook.Type == "Telegram" {
                services.SendTeleMessage(
                    context, event+"\n"+desc, webhook.TeleChatId)
            } else if webhook.Type == "Pushover" {
                services.SendPushoverMessage(
                    context, event+"\n"+desc, webhook.POUserKey)
            } else if webhook.Type == "Hipchat" {
                color := "red"
                if strings.Index(event, " success ") > -1 ||
                    strings.Index(event, " merged ") > -1 ||
                    strings.Index(event, " up") > -1 ||
                    strings.Index(event, "Ping!") > -1 {
                    color = "green"
                } else if strings.Index(event, " pull ") > -1 {
                    color = "yellow"
                }
                services.SendHipchatMessage(
                    context, event+"\n"+desc, webhook.HCRoomId,
                    webhook.HCToken, color)
            }
        }
        fmt.Fprintf(writer, "OK")
    }
}
<commit_msg>telegram bug fix<commit_after>// Main file

package webhook

import (
    "appengine"
    "appengine/datastore"
    "appengine/user"
    "encoding/json"
    "fmt"
    "github.com/gorilla/mux"
    "html/template"
    "net/http"
    "services"
    "strings"
    "time"
)

var redirectTmpl = template.Must(
    template.ParseFiles("templates/callback.html"))

// Initialize Appengine.
// Only routes, that's it!
func init() {
    route := mux.NewRouter()
    route.HandleFunc("/", root)
    route.HandleFunc("/cb", callback)
    route.HandleFunc("/connect", connect)
    route.HandleFunc("/created.json", createdJson)
    route.HandleFunc("/delete/{handler}", deleteHandler)
    route.HandleFunc("/login", login)
    route.HandleFunc("/redirect", redirect)
    route.HandleFunc("/save", save)
    route.HandleFunc("/telegram/{telegramToken}", telegramWebhook)
    route.HandleFunc("/trello/{type}/{boardid}", trelloList)
    route.HandleFunc("/w{handler}", hooks)
    http.Handle("/", route)
}

// Return list of created webhooks (/created.json)
func createdJson(writer http.ResponseWriter, request *http.Request) {
    context := appengine.NewContext(request)
    appUser := user.Current(context)
    webhooks := getWebhooks(context, appUser.Email)
    list, _ := json.Marshal(webhooks)
    writer.Header().Set("Content-Type", "application/json")
    fmt.Fprintf(writer, string(list))
}

// Redirect use to get trello service approval. (/connect)
func login(writer http.ResponseWriter, request *http.Request) {
    http.Redirect(writer, request, "/", http.StatusFound)
}

// Root handler (/), show for to create new and list of created hooks.
func root(writer http.ResponseWriter, request *http.Request) {
    context := appengine.NewContext(request)
    appUser := user.Current(context)
    if appUser != nil {
        listTmpl := template.Must(
            template.ParseFiles("templates/base.html", "templates/list.html"))
        url, _ := user.LogoutURL(context, "/")
        data := struct {
            AccessToken string
            Logout      string
        }{getAccessToken(context, appUser.Email), url}
        if err := listTmpl.Execute(writer, data); err != nil {
            http.Error(writer, err.Error(), http.StatusInternalServerError)
        }
    } else {
        homeTmpl := template.Must(template.ParseFiles("templates/index.html"))
        homeTmpl.Execute(writer, nil)
    }
}

// Redirect use to get trello service approval. (/connect)
func connect(writer http.ResponseWriter, request *http.Request) {
    http.Redirect(writer, request, services.GetAuthorizeUrl(), http.StatusFound)
}

// Once approval from service is done, read the token, make post request
// to callback handler (/cb) to save token.
func redirect(writer http.ResponseWriter, request *http.Request) {
    if err := redirectTmpl.Execute(writer, nil); err != nil {
        http.Error(writer, err.Error(), http.StatusInternalServerError)
    }
}

// Callback with token in post payload.
func callback(writer http.ResponseWriter, request *http.Request) {
    context := appengine.NewContext(request)
    appUser := user.Current(context)
    accessToken := AccessTokens{
        Email:       appUser.Email,
        AccessToken: request.FormValue("token"),
    }
    key := datastore.NewIncompleteKey(
        context, "AccessTokens", accessTokenKey(context, appUser.Email))
    _, err := datastore.Put(context, key, &accessToken)
    if err != nil {
        http.Error(writer, err.Error(), http.StatusInternalServerError)
        return
    }
    http.Redirect(writer, request, "/", http.StatusFound)
}

// Get list of trello boards or lists.
func trelloList(writer http.ResponseWriter, request *http.Request) {
    vars := mux.Vars(request)
    context := appengine.NewContext(request)
    appUser := user.Current(context)
    writer.Header().Set("Content-Type", "application/json")
    accessToken := getAccessToken(context, appUser.Email)
    if vars["type"] == "lists" {
        fmt.Fprintf(
            writer, services.GetBoardLists(
                context, vars["boardid"], accessToken))
        return
    }
    fmt.Fprintf(writer, services.GetBoards(context, accessToken))
}

// Save new hook from web.
func save(writer http.ResponseWriter, request *http.Request) {
    context := appengine.NewContext(request)
    appUser := user.Current(context)
    response := Response{
        Success: true,
        Reason:  "",
    }
    handler := "w" + services.GetAlphaNumberic(7)
    webhook := Webhook{
        User:    appUser.Email,
        Handler: handler,
        Date:    time.Now(),
        Count:   0,
    }
    if request.FormValue("service") == "trello" {
        webhook.Type = "Trello"
        webhook.BoardId = request.FormValue("boardId")
        webhook.BoardName = request.FormValue("boardName")
        webhook.ListId = request.FormValue("listId")
        webhook.ListName = request.FormValue("listName")
        services.PushToTrello(
            context, webhook.ListId,
            getAccessToken(context, webhook.User), "You are connected!", "")
    } else if request.FormValue("service") == "telegram" {
        webhook.Type = "Telegram"
        webhook.TeleChatId, webhook.TeleChatName = services.GetChatIdFromCode(
            context, request.FormValue("teleCode"))
        if webhook.TeleChatId == 0 {
            response.Success = false
            response.Reason = "Invalid code."
        } else {
            services.SendTeleMessage(
                context, "You are connected!", webhook.TeleChatId)
        }
    } else if request.FormValue("service") == "pushover" {
        webhook.Type = "Pushover"
        webhook.POUserKey = request.FormValue("poUserkey")
        status := services.SendPushoverMessage(
            context, "You are connected!", webhook.POUserKey)
        if status == 0 {
            response.Success = false
            response.Reason = "Invalid key."
        }
    } else if request.FormValue("service") == "hipchat" {
        webhook.Type = "Hipchat"
        webhook.HCToken = request.FormValue("hcToken")
        webhook.HCRoomId = request.FormValue("hcRoomid")
        status := services.SendHipchatMessage(
            context, "You are connected!", webhook.HCRoomId,
            webhook.HCToken, "green")
        if !status {
            response.Success = false
            response.Reason = "Invalid room id or token."
        }
    }
    if response.Success {
        key := datastore.NewIncompleteKey(
            context, "Webhook", webhookKey(context, handler))
        _, err := datastore.Put(context, key, &webhook)
        if err != nil {
            context.Infof("%v", err.Error())
            return
        }
        response.Handler = handler
    }
    writer.Header().Set("Content-Type", "application/json")
    resp, _ := json.Marshal(response)
    fmt.Fprintf(writer, string(resp))
}

func deleteHandler(writer http.ResponseWriter, request *http.Request) {
    vars := mux.Vars(request)
    context := appengine.NewContext(request)
    appUser := user.Current(context)
    response := Response{
        Success: false,
        Reason:  "Not found",
    }
    webhook := getWebhookFromHandler(context, vars["handler"])
    if webhook != nil && webhook.User == appUser.Email {
        response.Success = true
        response.Reason = ""
        deleteWebhookFromHandler(context, vars["handler"])
    }
    writer.Header().Set("Content-Type", "application/json")
    resp, _ := json.Marshal(response)
    fmt.Fprintf(writer, string(resp))
}

// Telegram webhook
func telegramWebhook(writer http.ResponseWriter, request *http.Request) {
    vars := mux.Vars(request)
    context := appengine.NewContext(request)
    decoder := json.NewDecoder(request.Body)
    fmt.Fprintf(
        writer, services.Telegram(context, decoder, vars["telegramToken"]))
}

// Actual webhook handler, receive events and post it to connected services.
func hooks(writer http.ResponseWriter, request *http.Request) {
    vars := mux.Vars(request)
    handler := "w" + vars["handler"]
    context := appengine.NewContext(request)
    webhook := getWebhookFromHandler(context, handler)
    if webhook != nil {
        event, desc := services.GetEventData(request)
        event = strings.Replace(event, "_", "\\_", -1)
        desc = strings.Replace(desc, "_", "\\_", -1)
        context.Infof("%s: %s \n %s", webhook.Type, event, desc)
        if event != "" {
            if webhook.Type == "Trello" {
                services.PushToTrello(
                    context, webhook.ListId,
                    getAccessToken(context, webhook.User), event, desc)
            } else if webhook.Type == "Telegram" {
                services.SendTeleMessage(
                    context, event+"\n"+desc, webhook.TeleChatId)
            } else if webhook.Type == "Pushover" {
                services.SendPushoverMessage(
                    context, event+"\n"+desc, webhook.POUserKey)
            } else if webhook.Type == "Hipchat" {
                color := "red"
                if strings.Index(event, " success ") > -1 ||
                    strings.Index(event, " merged ") > -1 ||
                    strings.Index(event, " up") > -1 ||
                    strings.Index(event, "Ping!") > -1 {
                    color = "green"
                } else if strings.Index(event, " pull ") > -1 {
                    color = "yellow"
                }
                services.SendHipchatMessage(
                    context, event+"\n"+desc, webhook.HCRoomId,
                    webhook.HCToken, color)
            }
        }
        fmt.Fprintf(writer, "OK")
    }
}
<|endoftext|>


<commit_before>package sqrl

import (
	"fmt"
	"math/big"

	"github.com/dustin/go-humanize"
	"github.com/likexian/host-stat-go"
	"github.com/shirou/gopsutil/mem"
)

type Ram struct {
	total, free, swapSpace uint64
}

type OS struct {
	hostname, kernel, version string
}

func ramOSInfo() (Ram, OS) {
	v, _ := mem.VirtualMemory()
	s, _ := mem.SwapMemory()
	i, _ := host_stat.GetHostInfo()
	o := OS{i.HostName, i.OSType, i.OSRelease}
	r := Ram{v.Total, v.Free, s.Free}
	return r, o
}
func printInfo() {
	mem, os := ramOSInfo()
	total := mem.total
	free := mem.free
	swap := mem.swapSpace
	totalint := int64(total)
	totalbig := big.NewInt(totalint)
	totalmb := humanize.BigBytes(totalbig)
	freeint := int64(free)
	freebig := big.NewInt(freeint)
	freemb := humanize.BigBytes(freebig)
	swapint := int64(swap)
	swapbig := big.NewInt(swapint)
	swapmb := humanize.BigBytes(swapbig)
	hostname := os.hostname
	kernel := os.kernel
	version := os.version
	fmt.Println()
	fmt.Println("RAM Info")
	fmt.Println("Total RAM: ", totalmb)
	fmt.Println("Free RAM: ", freemb)
	fmt.Println()
	fmt.Println("SwapSpace Info")
	fmt.Println("SwapSpace: ", swapmb)
	fmt.Println()
	fmt.Println("Version info")
	fmt.Println("OS Version: ", version)
	fmt.Println("Hostname: ", hostname)
	fmt.Println("Kernel Version: ", kernel)
	fmt.Println()
}
<commit_msg>Checked errors, changed hostname package, moved humanize out of printInfo function<commit_after>package sqrl

import (
	"fmt"
	"math/big"
	"os"

	"github.com/dustin/go-humanize"
	"github.com/likexian/host-stat-go"
	"github.com/shirou/gopsutil/mem"
)

type Ram struct {
	total, free, swapSpace string
}

type OS struct {
	hostname, kernel, version string
}

func ramOSInfo() (Ram, OS) {
	v, e := mem.VirtualMemory()
	if e != nil {
		fmt.Println(e)
	}
	s, err := mem.SwapMemory()
	if err != nil {
		fmt.Println(err)
	}
	i, er := host_stat.GetHostInfo()
	if er != nil {
		fmt.Println(er)
	}
	total := v.Total
	free := v.Free
	swap := s.Free
	totalint := int64(total)
	totalbig := big.NewInt(totalint)
	totalmb := humanize.BigBytes(totalbig)
	freeint := int64(free)
	freebig := big.NewInt(freeint)
	freemb := humanize.BigBytes(freebig)
	swapint := int64(swap)
	swapbig := big.NewInt(swapint)
	swapmb := humanize.BigBytes(swapbig)
	hostname, e := os.Hostname()
	if e != nil {
		fmt.Println(e)
	}
	kernel := i.OSType
	version := i.OSRelease
	r := Ram{totalmb, freemb, swapmb}
	o := OS{hostname, version, kernel}
	return r, o
}

func printInfo() {
	r, o := ramOSInfo()
	fmt.Println(r)
	fmt.Println(o)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"github.com/jmoiron/sqlx"
	"github.com/lib/pq"
)

func sqlInterface(
	rootAccount *Account,
	transactions []*Transaction,
) {

	dbDir := filepath.Join(os.TempDir(), fmt.Sprintf("keep-%d", rand.Int63()))
	if out, err := exec.Command("initdb", "-D", dbDir).CombinedOutput(); err != nil {
		fail("%v: %s", err, out)
	}
	pt("db dir: %s\n", dbDir)
	defer exec.Command("rm", "-rf", dbDir).Run()

	port := 10000 + rand.Intn(50000)
	conf := `
		port = ` + fmt.Sprintf("%d", port) + `
	`
	if err := ioutil.WriteFile(filepath.Join(dbDir, "postgresql.conf"), []byte(conf), 0644); err != nil {
		fail("%v", err)
	}
	c := exec.Command("postgres", "-D", dbDir)
	c.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
	}
	if err := c.Start(); err != nil {
		fail("%v", err)
	}
	defer syscall.Kill(-c.Process.Pid, syscall.SIGKILL)
	time.Sleep(time.Second)
	pt("db started\n")

	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://localhost:%d/postgres?sslmode=disable", port))
	if err != nil {
		fail("%v", err)
	}
	defer db.Close()
	tx := db.MustBegin()
	if _, err := tx.Exec(`
		CREATE TABLE entries (
			id bigserial primary key,
			transaction bigint,
			transaction_description text,
			date timestamp with time zone,
			account text[],
			currency text,
			amount numeric,
			description text
		)
		`,
	); err != nil {
		fail("%v", err)
	}
	for _, view := range views {
		if _, err := tx.Exec(view); err != nil {
			fail("%v", err)
		}
	}
	for tid, transaction := range transactions {
		for _, entry := range transaction.Entries {
			if _, err := tx.Exec(`
				INSERT INTO entries
				(
					transaction, transaction_description,
					date, account, currency, amount, description
				)
				VALUES (
					$1, $2,
					$3, $4, $5, $6::numeric, $7
				)
				`,
				tid,
				transaction.Description,
				entry.Time,
				func() (ret pq.StringArray) {
					acc := entry.Account
					for acc != rootAccount {
						ret = append(ret, acc.Name)
						acc = acc.Parent
					}
					for i := len(ret)/2 - 1; i >= 0; i-- {
						j := len(ret) - 1 - i
						ret[i], ret[j] = ret[j], ret[i]
					}
					return
				}(),
				entry.Currency,
				entry.Amount.FloatString(3),
				entry.Description,
			); err != nil {
				fail("%v", err)
			}
		}
	}
	if err := tx.Commit(); err != nil {
		fail("%v", err)
	}
	pt("data loaded\n")

	sigs := make(chan os.Signal)
	go func() {
		for {
			<-sigs
		}
	}()
	signal.Notify(sigs, os.Interrupt)

	psql := exec.Command("psql", fmt.Sprintf("postgres://localhost:%d/postgres", port))
	psql.Stdout = os.Stdout
	psql.Stdin = os.Stdin
	psql.Stderr = os.Stderr
	if err := psql.Run(); err != nil {
		fail("%v", err)
	}
}

var views = []string{
	// props
	`
	create view props as
	select distinct on (date, transaction)
	date, transaction_description
	from entries
	where account[1] = '支出'
	and account[2] in (
		'数码',
		'物品',
		'衣物服饰',
		'消耗品',
		'保健品',
		'书籍',
		'药物',
		'性用品'
	)
	order by date desc, transaction
	`,

	// consumables
	`
	create view consumables as
	select distinct on (date, transaction)
	date, transaction_description
	from entries
	where account[1] = '支出'
	and account[2] in (
		'饮食',
		'消耗品',
		'药物',
		'保健品'
	)
	order by date desc, transaction
	`,

	// yearly
	"create view yearly as" + intervalStat("date_trunc('year', date)"),

	// seasonally
	"create view seasonally as" + intervalStat("date_trunc('year', date) + interval '3 month' * (extract(month from date)::int / 3)"),

	// monthly
	"create view monthly as" + intervalStat("date_trunc('month', date)"),

	// weekly
	"create view weekly as" + intervalStat("date_trunc('week', date)"),

	// daily
	"create view daily as" + intervalStat("date_trunc('day', date)"),

	// this year expenses
	`
	create view this_year_expenses as
	select
	currency, sum(amount), account[2],
	jsonb_pretty(jsonb_agg(
			to_char(date, 'YYYY-MM-DD')
			|| ' '
			|| currency
			|| amount::numeric(10,2)
			|| ' '
			|| transaction_description
			order by amount desc, date desc
	))
	from entries
	where extract(year from date) = extract(year from now())
	and account[1] = '支出'
	group by account[2],currency
	order by sum desc
	`,

	//
}

func intervalStat(groupBy string) string {
	return `
	select
	to_char(` + groupBy + `, 'YYYY-MM-DD') as span,

	COALESCE(
		'支出' || currency || (sum(amount) filter (where account[1] = '支出'))::numeric(20,2)::text
		|| E'：\n'
		|| (
			select string_agg(
				currency || amount::numeric(20,2)::text || ' ' || account,
				E'\n'
				order by amount desc
			) from (
				select account[2] as account, currency, sum(amount) as amount
				from unnest(
					array_agg(id) filter (where account[1] = '支出')
				) as id
				join entries e2 using (id)
				group by account[2], currency
			) t0
		),
		'-'
	) || E'\n' as expenses,

	COALESCE(
		'收入' || currency || (sum(amount) filter (where account[1] = '收入'))::numeric(20,2)::text
		|| E'：\n'
		|| (
			select string_agg(
				currency || amount::numeric(20, 2)::text || ' ' || account,
				E'\n'
				order by amount asc
			) from (
				select account[2] as account, currency, sum(amount) as amount
				from unnest(
					array_agg(id) filter (where account[1] = '收入')
				) as id
				join entries e2 using (id)
				group by account[2], currency
			) t0
		),
		'-'
	) || E'\n' as income,

	COALESCE(
		'净资产' || currency || (
			-sum(amount) filter (where account[1] = '收入')
			-
			sum(amount) filter (where account[1] = '支出')
		)::numeric(20,2)::text,
		'-'
	) || E'\n' as net_income,

	COALESCE(
		E'资产：\n' || (
			select string_agg(
				currency
				|| amount::numeric(20, 2)::text || ' ' || account
				|| E'\n'
				|| '= 增' || pos_amount::numeric(20, 2)::text
				|| ' 减' || neg_amount::numeric(20, 2)::text,
				E'\n'
				order by amount desc
			) from (
				select account[2] as account, currency, sum(amount) as amount,
				COALESCE(sum(amount) filter (where amount >= 0), 0) as pos_amount,
				COALESCE(-sum(amount) filter (where amount < 0), 0) as neg_amount
				from unnest(
					array_agg(id) filter (where account[1] = '资产')
				) as id
				join entries e2 using (id)
				group by account[2], currency
			) t0
		),
		'-'
	) || E'\n' as equity,

	COALESCE(
		E'负债：\n' || (
			select string_agg(
				currency
				|| amount::numeric(20, 2)::text || ' ' || account
				|| E'\n'
				|| '= 还' || pos_amount::numeric(20, 2)::text
				|| ' 借' || neg_amount::numeric(20, 2)::text,
				E'\n'
				order by amount asc
			) from (
				select account[2] as account, currency, sum(amount) as amount,
				COALESCE(sum(amount) filter (where amount >= 0), 0) as pos_amount,
				COALESCE(-sum(amount) filter (where amount < 0), 0) as neg_amount
				from unnest(
					array_agg(id) filter (where account[1] = '负债')
				) as id
				join entries e2 using (id)
				group by account[2], currency
			) t0
		),
		'-'
	) || E'\n' as liability

	from
	entries

	group by ` + groupBy + `, currency
	order by span desc, currency asc
	`
}
<commit_msg>use copy to import data<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"github.com/jmoiron/sqlx"
	"github.com/lib/pq"
)

func sqlInterface(
	rootAccount *Account,
	transactions []*Transaction,
) {

	dbDir := filepath.Join(os.TempDir(), fmt.Sprintf("keep-%d", rand.Int63()))
	if out, err := exec.Command("initdb", "-D", dbDir).CombinedOutput(); err != nil {
		fail("%v: %s", err, out)
	}
	pt("db dir: %s\n", dbDir)
	defer exec.Command("rm", "-rf", dbDir).Run()

	port := 10000 + rand.Intn(50000)
	conf := `
		port = ` + fmt.Sprintf("%d", port) + `
	`
	if err := ioutil.WriteFile(filepath.Join(dbDir, "postgresql.conf"), []byte(conf), 0644); err != nil {
		fail("%v", err)
	}
	c := exec.Command("postgres", "-D", dbDir)
	c.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
	}
	if err := c.Start(); err != nil {
		fail("%v", err)
	}
	defer syscall.Kill(-c.Process.Pid, syscall.SIGKILL)
	time.Sleep(time.Second)
	pt("db started\n")

	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://localhost:%d/postgres?sslmode=disable", port))
	if err != nil {
		fail("%v", err)
	}
	defer db.Close()
	tx := db.MustBegin()
	if _, err := tx.Exec(`
		CREATE TABLE entries (
			id bigserial primary key,
			transaction bigint,
			transaction_description text,
			date timestamp with time zone,
			account text[],
			currency text,
			amount numeric,
			description text
		)
		`,
	); err != nil {
		fail("%v", err)
	}

	for _, view := range views {
		if _, err := tx.Exec(view); err != nil {
			fail("%v", err)
		}
	}

	stmt, err := tx.Prepare(pq.CopyIn("entries",
		"transaction", "transaction_description",
		"date", "account",
		"currency", "amount",
		"description",
	))
	if err != nil {
		panic(err)
	}
	for tid, transaction := range transactions {
		for _, entry := range transaction.Entries {
			if _, err := stmt.Exec(
				tid,
				transaction.Description,
				entry.Time,
				func() (ret pq.StringArray) {
					acc := entry.Account
					for acc != rootAccount {
						ret = append(ret, acc.Name)
						acc = acc.Parent
					}
					for i := len(ret)/2 - 1; i >= 0; i-- {
						j := len(ret) - 1 - i
						ret[i], ret[j] = ret[j], ret[i]
					}
					return
				}(),
				entry.Currency,
				entry.Amount.FloatString(3),
				entry.Description,
			); err != nil {
				panic(err)
			}
		}
	}
	if _, err := stmt.Exec(); err != nil {
		panic(err)
	}
	if err := stmt.Close(); err != nil {
		panic(err)
	}
	if err := tx.Commit(); err != nil {
		fail("%v", err)
	}
	pt("data loaded\n")

	sigs := make(chan os.Signal)
	go func() {
		for {
			<-sigs
		}
	}()
	signal.Notify(sigs, os.Interrupt)

	psql := exec.Command("psql", fmt.Sprintf("postgres://localhost:%d/postgres", port))
	psql.Stdout = os.Stdout
	psql.Stdin = os.Stdin
	psql.Stderr = os.Stderr
	if err := psql.Run(); err != nil {
		fail("%v", err)
	}
}

var views = []string{
	// props
	`
	create view props as
	select distinct on (date, transaction)
	date, transaction_description
	from entries
	where account[1] = '支出'
	and account[2] in (
		'数码',
		'物品',
		'衣物服饰',
		'消耗品',
		'保健品',
		'书籍',
		'药物',
		'性用品'
	)
	order by date desc, transaction
	`,

	// consumables
	`
	create view consumables as
	select distinct on (date, transaction)
	date, transaction_description
	from entries
	where account[1] = '支出'
	and account[2] in (
		'饮食',
		'消耗品',
		'药物',
		'保健品'
	)
	order by date desc, transaction
	`,

	// yearly
	"create view yearly as" + intervalStat("date_trunc('year', date)"),

	// seasonally
	"create view seasonally as" + intervalStat("date_trunc('year', date) + interval '3 month' * (extract(month from date)::int / 3)"),

	// monthly
	"create view monthly as" + intervalStat("date_trunc('month', date)"),

	// weekly
	"create view weekly as" + intervalStat("date_trunc('week', date)"),

	// daily
	"create view daily as" + intervalStat("date_trunc('day', date)"),

	// this year expenses
	`
	create view this_year_expenses as
	select
	currency, sum(amount), account[2],
	jsonb_pretty(jsonb_agg(
			to_char(date, 'YYYY-MM-DD')
			|| ' '
			|| currency
			|| amount::numeric(10,2)
			|| ' '
			|| transaction_description
			order by amount desc, date desc
	))
	from entries
	where extract(year from date) = extract(year from now())
	and account[1] = '支出'
	group by account[2],currency
	order by sum desc
	`,

	//
}

func intervalStat(groupBy string) string {
	return `
	select
	to_char(` + groupBy + `, 'YYYY-MM-DD') as span,

	COALESCE(
		'支出' || currency || (sum(amount) filter (where account[1] = '支出'))::numeric(20,2)::text
		|| E'：\n'
		|| (
			select string_agg(
				currency || amount::numeric(20,2)::text || ' ' || account,
				E'\n'
				order by amount desc
			) from (
				select account[2] as account, currency, sum(amount) as amount
				from unnest(
					array_agg(id) filter (where account[1] = '支出')
				) as id
				join entries e2 using (id)
				group by account[2], currency
			) t0
		),
		'-'
	) || E'\n' as expenses,

	COALESCE(
		'收入' || currency || (sum(amount) filter (where account[1] = '收入'))::numeric(20,2)::text
		|| E'：\n'
		|| (
			select string_agg(
				currency || amount::numeric(20, 2)::text || ' ' || account,
				E'\n'
				order by amount asc
			) from (
				select account[2] as account, currency, sum(amount) as amount
				from unnest(
					array_agg(id) filter (where account[1] = '收入')
				) as id
				join entries e2 using (id)
				group by account[2], currency
			) t0
		),
		'-'
	) || E'\n' as income,

	COALESCE(
		'净资产' || currency || (
			-sum(amount) filter (where account[1] = '收入')
			-
			sum(amount) filter (where account[1] = '支出')
		)::numeric(20,2)::text,
		'-'
	) || E'\n' as net_income,

	COALESCE(
		E'资产：\n' || (
			select string_agg(
				currency
				|| amount::numeric(20, 2)::text || ' ' || account
				|| E'\n'
				|| '= 增' || pos_amount::numeric(20, 2)::text
				|| ' 减' || neg_amount::numeric(20, 2)::text,
				E'\n'
				order by amount desc
			) from (
				select account[2] as account, currency, sum(amount) as amount,
				COALESCE(sum(amount) filter (where amount >= 0), 0) as pos_amount,
				COALESCE(-sum(amount) filter (where amount < 0), 0) as neg_amount
				from unnest(
					array_agg(id) filter (where account[1] = '资产')
				) as id
				join entries e2 using (id)
				group by account[2], currency
			) t0
		),
		'-'
	) || E'\n' as equity,

	COALESCE(
		E'负债：\n' || (
			select string_agg(
				currency
				|| amount::numeric(20, 2)::text || ' ' || account
				|| E'\n'
				|| '= 还' || pos_amount::numeric(20, 2)::text
				|| ' 借' || neg_amount::numeric(20, 2)::text,
				E'\n'
				order by amount asc
			) from (
				select account[2] as account, currency, sum(amount) as amount,
				COALESCE(sum(amount) filter (where amount >= 0), 0) as pos_amount,
				COALESCE(-sum(amount) filter (where amount < 0), 0) as neg_amount
				from unnest(
					array_agg(id) filter (where account[1] = '负债')
				) as id
				join entries e2 using (id)
				group by account[2], currency
			) t0
		),
		'-'
	) || E'\n' as liability

	from
	entries

	group by ` + groupBy + `, currency
	order by span desc, currency asc
	`
}
<|endoftext|>


<commit_before>package sqlx

import (
	"bytes"
	"errors"
	"reflect"
	"strconv"
	"strings"

	"github.com/jmoiron/sqlx/reflectx"
)

// Bindvar types supported by Rebind, BindMap and BindStruct.
const (
	UNKNOWN = iota
	QUESTION
	DOLLAR
	NAMED
)

// BindType returns the bindtype for a given database given a drivername.
func BindType(driverName string) int {
	switch driverName {
	case "postgres", "pgx":
		return DOLLAR
	case "mysql":
		return QUESTION
	case "sqlite3":
		return QUESTION
	case "oci8", "ora", "goracle":
		return NAMED
	}
	return UNKNOWN
}

// FIXME: this should be able to be tolerant of escaped ?'s in queries without
// losing much speed, and should be to avoid confusion.

// Rebind a query from the default bindtype (QUESTION) to the target bindtype.
func Rebind(bindType int, query string) string {
	switch bindType {
	case QUESTION, UNKNOWN:
		return query
	}

	// Add space enough for 10 params before we have to allocate
	rqb := make([]byte, 0, len(query)+10)

	var i, j int

	for i = strings.Index(query, "?"); i != -1; i = strings.Index(query, "?") {
		rqb = append(rqb, query[:i]...)

		switch bindType {
		case DOLLAR:
			rqb = append(rqb, '$')
		case NAMED:
			rqb = append(rqb, ':', 'a', 'r', 'g')
		}

		j++
		rqb = strconv.AppendInt(rqb, int64(j), 10)

		query = query[i+1:]
	}

	return string(append(rqb, query...))
}

// Experimental implementation of Rebind which uses a bytes.Buffer.  The code is
// much simpler and should be more resistant to odd unicode, but it is twice as
// slow.  Kept here for benchmarking purposes and to possibly replace Rebind if
// problems arise with its somewhat naive handling of unicode.
func rebindBuff(bindType int, query string) string {
	if bindType != DOLLAR {
		return query
	}

	b := make([]byte, 0, len(query))
	rqb := bytes.NewBuffer(b)
	j := 1
	for _, r := range query {
		if r == '?' {
			rqb.WriteRune('$')
			rqb.WriteString(strconv.Itoa(j))
			j++
		} else {
			rqb.WriteRune(r)
		}
	}

	return rqb.String()
}

// In expands slice values in args, returning the modified query string
// and a new arg list that can be executed by a database. The `query` should
// use the `?` bindVar.  The return value uses the `?` bindVar.
func In(query string, args ...interface{}) (string, []interface{}, error) {
	// argMeta stores reflect.Value and length for slices and
	// the value itself for non-slice arguments
	type argMeta struct {
		v      reflect.Value
		i      interface{}
		length int
	}

	var flatArgsCount int
	var anySlices bool

	meta := make([]argMeta, len(args))

	for i, arg := range args {
		v := reflect.ValueOf(arg)
		t := reflectx.Deref(v.Type())

		if t.Kind() == reflect.Slice {
			meta[i].length = v.Len()
			meta[i].v = v

			anySlices = true
			flatArgsCount += meta[i].length

			if meta[i].length == 0 {
				return "", nil, errors.New("empty slice passed to 'in' query")
			}
		} else {
			meta[i].i = arg
			flatArgsCount++
		}
	}

	// don't do any parsing if there aren't any slices;  note that this means
	// some errors that we might have caught below will not be returned.
	if !anySlices {
		return query, args, nil
	}

	newArgs := make([]interface{}, 0, flatArgsCount)
	buf := bytes.NewBuffer(make([]byte, 0, len(query)+len(", ?")*flatArgsCount))

	var arg, offset int

	for i := strings.IndexByte(query[offset:], '?'); i != -1; i = strings.IndexByte(query[offset:], '?') {
		if arg >= len(meta) {
			// if an argument wasn't passed, lets return an error;  this is
			// not actually how database/sql Exec/Query works, but since we are
			// creating an argument list programmatically, we want to be able
			// to catch these programmer errors earlier.
			return "", nil, errors.New("number of bindVars exceeds arguments")
		}

		argMeta := meta[arg]
		arg++

		// not a slice, continue.
		// our questionmark will either be written before the next expansion
		// of a slice or after the loop when writing the rest of the query
		if argMeta.length == 0 {
			offset = offset + i + 1
			newArgs = append(newArgs, argMeta.i)
			continue
		}

		// write everything up to and including our ? character
		buf.WriteString(query[:offset+i+1])

		for si := 1; si < argMeta.length; si++ {
			buf.WriteString(", ?")
		}

		newArgs = appendReflectSlice(newArgs, argMeta.v, argMeta.length)

		// slice the query and reset the offset. this avoids some bookkeeping for
		// the write after the loop
		query = query[offset+i+1:]
		offset = 0
	}

	buf.WriteString(query)

	if arg < len(meta) {
		return "", nil, errors.New("number of bindVars less than number arguments")
	}

	return buf.String(), newArgs, nil
}

func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} {
	switch val := v.Interface().(type) {
	case []interface{}:
		args = append(args, val...)
	case []int:
		for i := range val {
			args = append(args, val[i])
		}
	case []string:
		for i := range val {
			args = append(args, val[i])
		}
	default:
		for si := 0; si < vlen; si++ {
			args = append(args, v.Index(si).Interface())
		}
	}

	return args
}
<commit_msg>Valuer objects are evaluated before In expansion<commit_after>package sqlx

import (
	"bytes"
	"errors"
	"reflect"
	"strconv"
	"strings"

	"github.com/jmoiron/sqlx/reflectx"
)

// Bindvar types supported by Rebind, BindMap and BindStruct.
const (
	UNKNOWN = iota
	QUESTION
	DOLLAR
	NAMED
)

// BindType returns the bindtype for a given database given a drivername.
func BindType(driverName string) int {
	switch driverName {
	case "postgres", "pgx":
		return DOLLAR
	case "mysql":
		return QUESTION
	case "sqlite3":
		return QUESTION
	case "oci8", "ora", "goracle":
		return NAMED
	}
	return UNKNOWN
}

// FIXME: this should be able to be tolerant of escaped ?'s in queries without
// losing much speed, and should be to avoid confusion.

// Rebind a query from the default bindtype (QUESTION) to the target bindtype.
func Rebind(bindType int, query string) string {
	switch bindType {
	case QUESTION, UNKNOWN:
		return query
	}

	// Add space enough for 10 params before we have to allocate
	rqb := make([]byte, 0, len(query)+10)

	var i, j int

	for i = strings.Index(query, "?"); i != -1; i = strings.Index(query, "?") {
		rqb = append(rqb, query[:i]...)

		switch bindType {
		case DOLLAR:
			rqb = append(rqb, '$')
		case NAMED:
			rqb = append(rqb, ':', 'a', 'r', 'g')
		}

		j++
		rqb = strconv.AppendInt(rqb, int64(j), 10)

		query = query[i+1:]
	}

	return string(append(rqb, query...))
}

// Experimental implementation of Rebind which uses a bytes.Buffer.  The code is
// much simpler and should be more resistant to odd unicode, but it is twice as
// slow.  Kept here for benchmarking purposes and to possibly replace Rebind if
// problems arise with its somewhat naive handling of unicode.
func rebindBuff(bindType int, query string) string {
	if bindType != DOLLAR {
		return query
	}

	b := make([]byte, 0, len(query))
	rqb := bytes.NewBuffer(b)
	j := 1
	for _, r := range query {
		if r == '?' {
			rqb.WriteRune('$')
			rqb.WriteString(strconv.Itoa(j))
			j++
		} else {
			rqb.WriteRune(r)
		}
	}

	return rqb.String()
}

// In expands slice values in args, returning the modified query string
// and a new arg list that can be executed by a database. The `query` should
// use the `?` bindVar.  The return value uses the `?` bindVar.
func In(query string, args ...interface{}) (string, []interface{}, error) {
	// argMeta stores reflect.Value and length for slices and
	// the value itself for non-slice arguments
	type argMeta struct {
		v      reflect.Value
		i      interface{}
		length int
	}

	var flatArgsCount int
	var anySlices bool

	meta := make([]argMeta, len(args))

	for i, arg := range args {
		if a, ok := arg.(driver.Valuer); ok {
			arg, _ = a.Value()
		}
		v := reflect.ValueOf(arg)
		t := reflectx.Deref(v.Type())

		if t.Kind() == reflect.Slice {
			meta[i].length = v.Len()
			meta[i].v = v

			anySlices = true
			flatArgsCount += meta[i].length

			if meta[i].length == 0 {
				return "", nil, errors.New("empty slice passed to 'in' query")
			}
		} else {
			meta[i].i = arg
			flatArgsCount++
		}
	}

	// don't do any parsing if there aren't any slices;  note that this means
	// some errors that we might have caught below will not be returned.
	if !anySlices {
		return query, args, nil
	}

	newArgs := make([]interface{}, 0, flatArgsCount)
	buf := bytes.NewBuffer(make([]byte, 0, len(query)+len(", ?")*flatArgsCount))

	var arg, offset int

	for i := strings.IndexByte(query[offset:], '?'); i != -1; i = strings.IndexByte(query[offset:], '?') {
		if arg >= len(meta) {
			// if an argument wasn't passed, lets return an error;  this is
			// not actually how database/sql Exec/Query works, but since we are
			// creating an argument list programmatically, we want to be able
			// to catch these programmer errors earlier.
			return "", nil, errors.New("number of bindVars exceeds arguments")
		}

		argMeta := meta[arg]
		arg++

		// not a slice, continue.
		// our questionmark will either be written before the next expansion
		// of a slice or after the loop when writing the rest of the query
		if argMeta.length == 0 {
			offset = offset + i + 1
			newArgs = append(newArgs, argMeta.i)
			continue
		}

		// write everything up to and including our ? character
		buf.WriteString(query[:offset+i+1])

		for si := 1; si < argMeta.length; si++ {
			buf.WriteString(", ?")
		}

		newArgs = appendReflectSlice(newArgs, argMeta.v, argMeta.length)

		// slice the query and reset the offset. this avoids some bookkeeping for
		// the write after the loop
		query = query[offset+i+1:]
		offset = 0
	}

	buf.WriteString(query)

	if arg < len(meta) {
		return "", nil, errors.New("number of bindVars less than number arguments")
	}

	return buf.String(), newArgs, nil
}

func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} {
	switch val := v.Interface().(type) {
	case []interface{}:
		args = append(args, val...)
	case []int:
		for i := range val {
			args = append(args, val[i])
		}
	case []string:
		for i := range val {
			args = append(args, val[i])
		}
	default:
		for si := 0; si < vlen; si++ {
			args = append(args, v.Index(si).Interface())
		}
	}

	return args
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import (
	`fmt`
	`regexp`
)

const (
	isCapture   = 0x00F00000
	isPromo     = 0x0F000000
	isCastle    = 0x10000000
	isEnpassant = 0x20000000
)

// Bits 00:00:00:FF => Source square (0 .. 63).
// Bits 00:00:FF:00 => Destination square (0 .. 63).
// Bits 00:0F:00:00 => Piece making the move.
// Bits 00:F0:00:00 => Captured piece if any.
// Bits 0F:00:00:00 => Promoted piece if any.
// Bits F0:00:00:00 => Castle and en-passant flags.
type Move uint32

func NewMove(p *Position, from, to int) Move {
	piece, capture := p.pieces[from], p.pieces[to]

	if p.enpassant != 0 && to == p.enpassant {
		capture = pawn(piece.color() ^ 1)
	}

	return Move(from | (to << 8) | (int(piece) << 16) | (int(capture) << 20))
}

func NewPawnMove(p *Position, square, target int) Move {
	if Abs(square - target) == 16 {

		// Check if pawn jump causes en-passant. This is done by verifying
		// whether enemy pawns occupy squares ajacent to the target square.
		pawns := p.outposts[pawn(p.color ^ 1)]
		if pawns & maskIsolated[Col(target)] & maskRank[Row(target)] != 0 {
			return NewEnpassant(p, square, target)
		}
	}

	return NewMove(p, square, target)
}

func NewEnpassant(p *Position, from, to int) Move {
	return Move(from | (to << 8) | (int(p.pieces[from]) << 16) | isEnpassant)
}

func NewCastle(p *Position, from, to int) Move {
	return Move(from | (to << 8) | (int(p.pieces[from]) << 16) | isCastle)
}

func NewPromotion(p *Position, square, target int) (Move, Move, Move, Move) {
	return NewMove(p, square, target).promote(Queen),
	       NewMove(p, square, target).promote(Rook),
	       NewMove(p, square, target).promote(Bishop),
	       NewMove(p, square, target).promote(Knight)
}

func NewMoveFromString(p *Position, e2e4 string) (move Move) {
	re := regexp.MustCompile(`([KkQqRrBbNn]?)([a-h])([1-8])-?([a-h])([1-8])([QqRrBbNn]?)`)
	arr := re.FindStringSubmatch(e2e4)

	if len(arr) > 0 {
		name := arr[1]
		from := Square(int(arr[3][0]-'1'), int(arr[2][0]-'a'))
		to := Square(int(arr[5][0]-'1'), int(arr[4][0]-'a'))
		promo := arr[6]

		var piece Piece
		switch name {
		case `K`, `k`:
			piece = king(p.color)
		case `Q`, `q`:
			piece = queen(p.color)
		case `R`, `r`:
			piece = rook(p.color)
		case `B`, `b`:
			piece = bishop(p.color)
		case `N`, `n`:
			piece = knight(p.color)
		default:
			piece = p.pieces[from] // <-- Makes piece character optional.
		}
		if (p.pieces[from] != piece) || (p.targets(from)&bit[to] == 0) {
			move = 0 // Invalid move.
		} else {
			move = NewMove(p, from, to)
			if len(promo) > 0 {
				switch promo {
				case `Q`, `q`:
					move = move.promote(Queen)
				case `R`, `r`:
					move = move.promote(Rook)
				case `B`, `b`:
					move = move.promote(Bishop)
				case `N`, `n`:
					move = move.promote(Knight)
				default:
					move = 0
				}
			}
		}
	} else if e2e4 == `0-0` || e2e4 == `0-0-0` {
		from := p.king[p.color]
		to := G1
		if e2e4 == `0-0-0` {
			to = C1
		}
		if p.color == Black {
			to += 56
		}
		move = NewCastle(p, from, to)
		if !move.isCastle() {
			move = 0
		}
	}
	return
}

func (m Move) from() int {
	return int(m & 0xFF)
}

func (m Move) to() int {
	return int((m >> 8) & 0xFF)
}

func (m Move) piece() Piece {
	return Piece((m >> 16) & 0x0F)
}

func (m Move) color() int {
	return int((m >> 16) & 1)
}

func (m Move) capture() Piece {
	return Piece((m >> 20) & 0x0F)
}

func (m Move) split() (from, to int, piece, capture Piece) {
	return int(m & 0xFF), int((m >> 8) & 0xFF), Piece((m >> 16) & 0x0F), Piece((m >> 20) & 0x0F)
}

func (m Move) promo() Piece {
	return Piece((m >> 24) & 0x0F)
}

func (m Move) promote(kind int) Move {
	piece := Piece(kind | m.color())
	return m | Move(int(piece) << 24)
}

func (m Move) isCastle() bool {
	return m&isCastle != 0
}

func (m Move) castle() Move {
	return m | isCastle
}

func (m Move) isEnpassant() bool {
	return m & isEnpassant != 0
}

// Returns true if the move doesn't change material balance.
func (m Move) isQuiet() bool {
	return m & (isCapture | isPromo) == 0
}

func (m Move) enpassant() Move {
	return m | isEnpassant
}

// Capture value based on most valueable victim/least valueable attacker.
func (m Move) value() int {
	return pieceValue[m.capture()] - m.piece().kind()
}

func (m Move) String() string {
	from, to, piece, capture := m.split()
	promo := m.promo().s()

	if (piece == King && from == E1 && to == G1) || (piece == BlackKing && from == E8 && to == G8) {
		return `0-0`
	} else if (piece == King && from == E1 && to == C1) || (piece == BlackKing && from == E8 && to == C8) {
		return `0-0-0`
	} else {
		col := [2]int{Col(from) + 'a', Col(to) + 'a'}
		row := [2]int{Row(from) + 1, Row(to) + 1}

		sign := '-'
		if capture != 0 || (piece.isPawn() && Col(from) != Col(to)) {
			sign = 'x'
		}

		format := `%c%d%c%c%d%s`
		if piece.isPawn() { // Skip piece name if it's a pawn.
			return fmt.Sprintf(format, col[0], row[0], sign, col[1], row[1], promo)
		} else {
			if Settings.Fancy {
				// Fancy notation is more readable with extra space.
				return fmt.Sprintf(`%s `+format, piece, col[0], row[0], sign, col[1], row[1], promo)
			} else {
				// Use uppercase letter to representa a piece regardless of its color.
				return fmt.Sprintf(`%s`+format, piece.s(), col[0], row[0], sign, col[1], row[1], promo)
			}
		}
	}
}
<commit_msg>Simpler move to string<commit_after>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import (
	`fmt`
	`regexp`
)

const (
	isCapture   = 0x00F00000
	isPromo     = 0x0F000000
	isCastle    = 0x10000000
	isEnpassant = 0x20000000
)

// Bits 00:00:00:FF => Source square (0 .. 63).
// Bits 00:00:FF:00 => Destination square (0 .. 63).
// Bits 00:0F:00:00 => Piece making the move.
// Bits 00:F0:00:00 => Captured piece if any.
// Bits 0F:00:00:00 => Promoted piece if any.
// Bits F0:00:00:00 => Castle and en-passant flags.
type Move uint32

func NewMove(p *Position, from, to int) Move {
	piece, capture := p.pieces[from], p.pieces[to]

	if p.enpassant != 0 && to == p.enpassant {
		capture = pawn(piece.color() ^ 1)
	}

	return Move(from | (to << 8) | (int(piece) << 16) | (int(capture) << 20))
}

func NewPawnMove(p *Position, square, target int) Move {
	if Abs(square - target) == 16 {

		// Check if pawn jump causes en-passant. This is done by verifying
		// whether enemy pawns occupy squares ajacent to the target square.
		pawns := p.outposts[pawn(p.color ^ 1)]
		if pawns & maskIsolated[Col(target)] & maskRank[Row(target)] != 0 {
			return NewEnpassant(p, square, target)
		}
	}

	return NewMove(p, square, target)
}

func NewEnpassant(p *Position, from, to int) Move {
	return Move(from | (to << 8) | (int(p.pieces[from]) << 16) | isEnpassant)
}

func NewCastle(p *Position, from, to int) Move {
	return Move(from | (to << 8) | (int(p.pieces[from]) << 16) | isCastle)
}

func NewPromotion(p *Position, square, target int) (Move, Move, Move, Move) {
	return NewMove(p, square, target).promote(Queen),
	       NewMove(p, square, target).promote(Rook),
	       NewMove(p, square, target).promote(Bishop),
	       NewMove(p, square, target).promote(Knight)
}

func NewMoveFromString(p *Position, e2e4 string) (move Move) {
	re := regexp.MustCompile(`([KkQqRrBbNn]?)([a-h])([1-8])-?([a-h])([1-8])([QqRrBbNn]?)`)
	arr := re.FindStringSubmatch(e2e4)

	if len(arr) > 0 {
		name := arr[1]
		from := Square(int(arr[3][0]-'1'), int(arr[2][0]-'a'))
		to := Square(int(arr[5][0]-'1'), int(arr[4][0]-'a'))
		promo := arr[6]

		var piece Piece
		switch name {
		case `K`, `k`:
			piece = king(p.color)
		case `Q`, `q`:
			piece = queen(p.color)
		case `R`, `r`:
			piece = rook(p.color)
		case `B`, `b`:
			piece = bishop(p.color)
		case `N`, `n`:
			piece = knight(p.color)
		default:
			piece = p.pieces[from] // <-- Makes piece character optional.
		}
		if (p.pieces[from] != piece) || (p.targets(from)&bit[to] == 0) {
			move = 0 // Invalid move.
		} else {
			move = NewMove(p, from, to)
			if len(promo) > 0 {
				switch promo {
				case `Q`, `q`:
					move = move.promote(Queen)
				case `R`, `r`:
					move = move.promote(Rook)
				case `B`, `b`:
					move = move.promote(Bishop)
				case `N`, `n`:
					move = move.promote(Knight)
				default:
					move = 0
				}
			}
		}
	} else if e2e4 == `0-0` || e2e4 == `0-0-0` {
		from := p.king[p.color]
		to := G1
		if e2e4 == `0-0-0` {
			to = C1
		}
		if p.color == Black {
			to += 56
		}
		move = NewCastle(p, from, to)
		if !move.isCastle() {
			move = 0
		}
	}
	return
}

func (m Move) from() int {
	return int(m & 0xFF)
}

func (m Move) to() int {
	return int((m >> 8) & 0xFF)
}

func (m Move) piece() Piece {
	return Piece((m >> 16) & 0x0F)
}

func (m Move) color() int {
	return int((m >> 16) & 1)
}

func (m Move) capture() Piece {
	return Piece((m >> 20) & 0x0F)
}

func (m Move) split() (from, to int, piece, capture Piece) {
	return int(m & 0xFF), int((m >> 8) & 0xFF), Piece((m >> 16) & 0x0F), Piece((m >> 20) & 0x0F)
}

func (m Move) promo() Piece {
	return Piece((m >> 24) & 0x0F)
}

func (m Move) promote(kind int) Move {
	piece := Piece(kind | m.color())
	return m | Move(int(piece) << 24)
}

func (m Move) castle() Move {
	return m | isCastle
}

func (m Move) enpassant() Move {
	return m | isEnpassant
}

// Capture value based on most valueable victim/least valueable attacker.
func (m Move) value() int {
	return pieceValue[m.capture()] - m.piece().kind()
}

func (m Move) isCastle() bool {
	return m & isCastle != 0
}

func (m Move) isEnpassant() bool {
	return m & isEnpassant != 0
}

// Returns true if the move doesn't change material balance.
func (m Move) isQuiet() bool {
	return m & (isCapture | isPromo) == 0
}

func (m Move) String() string {
	from, to, piece, capture := m.split()
	promo := m.promo().s()

	if m.isCastle() {
		if to > from {
			return `0-0`
		}
		return `0-0-0`
	}

	col := [2]int{Col(from) + 'a', Col(to) + 'a'}
	row := [2]int{Row(from) + 1, Row(to) + 1}

	sign := '-'
	if capture != 0 || (piece.isPawn() && Col(from) != Col(to)) {
		sign = 'x'
	}

	format := `%c%d%c%c%d%s`
	if piece.isPawn() { // Skip piece name if it's a pawn.
		return fmt.Sprintf(format, col[0], row[0], sign, col[1], row[1], promo)
	} else {
		if Settings.Fancy {
			// Fancy notation is more readable with extra space.
			return fmt.Sprintf(`%s `+format, piece, col[0], row[0], sign, col[1], row[1], promo)
		} else {
			// Use uppercase letter to representa a piece regardless of its color.
			return fmt.Sprintf(`%s`+format, piece.s(), col[0], row[0], sign, col[1], row[1], promo)
		}
	}
}
<|endoftext|>


<commit_before>// myLG is command line looking glass that written with Go language
// it tries from its own icmp and external looking glasses tools
package main

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/briandowns/spinner"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/disc"
	"github.com/mehrdadrad/mylg/http/ping"
	"github.com/mehrdadrad/mylg/icmp"
	"github.com/mehrdadrad/mylg/lg"
	"github.com/mehrdadrad/mylg/ns"
	"github.com/mehrdadrad/mylg/packet"
	"github.com/mehrdadrad/mylg/peeringdb"
	"github.com/mehrdadrad/mylg/scan"
	"github.com/mehrdadrad/mylg/services/httpd"
	"github.com/mehrdadrad/mylg/whois"
)

const (
	version = "0.2.4"
)

// Provider represents looking glass
type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() []string
	ChangeNode(node string) bool
	Ping() (string, error)
	Trace() chan string
	BGP() chan string
}

var (
	pNames = providerNames()
	req    = make(chan string, 1)
	nxt    = make(chan struct{}, 1)
	spin   = spinner.New(spinner.CharSets[26], 220*time.Millisecond)
	eArgs  = os.Args
	args   string
	prompt string
	cPName string
	noIf   bool = true
	cfg    cli.Config
	nsr    *ns.Request
	c      *cli.Readline

	// register looking glass hosts
	providers = map[string]Provider{
		"telia":  new(lg.Telia),  // telia
		"level3": new(lg.Level3), // level3
		"cogent": new(lg.Cogent), //cogent
	}

	// map cmd to function
	cmdFunc = map[string]func(){
		"web":     web,          // web dashboard
		"dump":    dump,         // dump traffic
		"disc":    discovery,    // network discovery
		"scan":    scanPorts,    // network scan
		"mode":    mode,         // editor mode
		"ping":    pingQuery,    // ping
		"trace":   trace,        // trace route
		"bgp":     BGP,          // BGP
		"whois":   whoisLookup,  // whois / dns lookup
		"peering": peeringDB,    // peering DB
		"hping":   hping,        // hping
		"dig":     dig,          // dig
		"node":    node,         // change node
		"connect": connect,      // connect to a country or LG
		"local":   local,        // local
		"help":    help,         // help
		"exit":    cleanUp,      // clean up
		"quit":    cleanUp,      // clean up
		"show":    show,         // show config
		"set":     setConfig,    // set config
		"lg":      setLG,        // prepare looking glass
		"ns":      setNS,        // prepare name server
		"version": printVersion, // prints version
	}
)

// init
func init() {
	// load configuration
	cfg = cli.LoadConfig()
	// with interface
	if len(eArgs) == 1 {
		// initialize cli
		c = cli.Init(version)
		go c.Run(req, nxt)
		// start web server
		go httpd.Run(cfg)
		// set interface enabled
		noIf = false
	}
	// initialize name server
	nsr = ns.NewRequest()
	go nsr.Init()
	// set local as default
	local()
}

func main() {
	// command line w/o interface
	if noIf {
		cmd := eArgs[1]
		args = strings.Join(eArgs[2:], " ")
		if f, ok := cmdFunc[cmd]; ok {
			f()
		} else {
			println("Invalid command please try mylg help")
		}
		return
	}
	// command like w/ interface
LOOP:
	for {
		select {
		case request, ok := <-req:
			if !ok {
				break LOOP
			}
			if len(request) < 1 {
				c.Next()
				continue
			}
			subReq := cli.CMDRgx().FindStringSubmatch(request)
			if len(subReq) == 0 {
				println("syntax error")
				c.Next()
				continue
			}
			prompt = c.GetPrompt()
			args = strings.TrimSpace(subReq[2])
			cmd := strings.TrimSpace(subReq[1])
			if f, ok := cmdFunc[cmd]; ok {
				f()
			} else {
				println("Invalid command please try help")
			}
			c.Next()
		}
	}
}

// providerName
func providerNames() []string {
	pNames := []string{}
	for p := range providers {
		pNames = append(pNames, p)
	}
	return pNames
}

// validateProvider
func validateProvider(p string) (string, error) {
	pNames := []string{}
	match, _ := regexp.MatchString("("+strings.Join(pNames, "|")+")", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	}
	return "", errors.New("provider not support")

}

// node handles node cmd
func node() {
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if _, ok := providers[cPName]; ok {
			if providers[cPName].ChangeNode(args) {
				c.UpdatePromptN(args, 3)
				return
			}
		}
		println("the specified node doesn't support")
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkNode(args) {
			println("error: argument is not valid")
		} else {
			c.UpdatePromptN(args, 3)
		}
	default:
		if cPName == "local" {
			println("local doesn't support node")
		}

	}
}

// dig gets dig info
func dig() {
	if ok := nsr.SetOptions(args, prompt); ok {
		nsr.Dig()
	}
}

// web tries to open web interface at default web browser
func web() {
	var openCmd = "open"
	println("opening default web broswer ...")
	if runtime.GOOS != "darwin" {
		openCmd = "xdg-open"
	}
	cmd := exec.Command(openCmd, fmt.Sprintf("http://%s:%d", cfg.Web.Address, cfg.Web.Port))
	err := cmd.Start()
	if err != nil {
		println("error opening default browser")
	}

}

// dump provides decoding packets
func dump() {
	p, err := packet.NewPacket(args)
	if p == nil || err != nil {
		return
	}
	println(p.Banner())
	for l := range p.Open() {
		l.PrintPretty()
	}
}

// connect handles connect cmd
func connect() {
	var (
		pName string
		err   error
	)
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if pName, err = validateProvider(args); err != nil {
			println("provider not available")
			c.Next()
			return
		}
		cPName = pName
		if _, ok := providers[cPName]; ok {
			c.UpdatePromptN(cPName+"/"+providers[cPName].GetDefaultNode(), 2)
			go func() {
				c.UpdateCompleter("node", providers[cPName].GetNodes())
			}()
		} else {
			println("it doesn't support")
		}
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkCountry(args) {
			println("error: argument is not valid")
		} else {
			c.SetPrompt("ns/" + args)
			c.UpdateCompleter("node", nsr.NodeList())
		}
	}
}

// mode set editor mode
func mode() {
	if args == "vim" {
		c.SetVim()
	} else if args == "emacs" {
		c.SetEmacs()
	} else {
		println("the request mode doesn't support")
	}
}

// trace tries to trace from local and lg
func trace() {
	switch {
	case strings.HasPrefix(prompt, "local"):
		trace, err := icmp.NewTrace(args, cfg)
		if err != nil {
			println(err.Error())
		}
		if trace == nil {
			break
		}
		trace.Print()
	case strings.HasPrefix(prompt, "lg"):
		providers[cPName].Set(args, "ipv4")
		for l := range providers[cPName].Trace() {
			println(l)
		}
	}
}

// hping tries to ping a web server by http
func hping() {
	// it should work at local mode
	if cPName != "local" {
		return
	}
	p, err := ping.NewPing(args, cfg)
	if err != nil {
		println(err.Error())
	} else {
		p.Run()
	}
}

// pingQuery runs ping command (local/LG)
func pingQuery() {
	if cPName == "local" {
		pingLocal()
	} else {
		pingLG()
	}
}

// pingLG tries to ping through a looking glass
func pingLG() {
	spin.Prefix = "please wait "
	spin.Start()
	providers[cPName].Set(args, "ipv4")
	m, err := providers[cPName].Ping()
	spin.Stop()
	if err != nil {
		println(err.Error())
	} else {
		println(m)
	}
}

// pingLocal tries to ping from local source ip
func pingLocal() {
	p, err := icmp.NewPing(args, cfg)
	if err != nil {
		println(err.Error())
	}
	if p == nil {
		return
	}
	if !p.IsCIDR() {
		resp := p.Run()
		p.PrintPretty(resp)
	}
}

// scanPorts tries to scan tcp/ip ports
func scanPorts() {
	scan, err := scan.NewScan(args, cfg)
	if err != nil {
		println(err.Error())
	} else {
		scan.Run()
	}
}

// BGP tries to get BGP lookup from a LG
func BGP() {
	if cPName == "local" {
		println("no provider selected")
		return
	}
	providers[cPName].Set(args, "ipv4")
	for l := range providers[cPName].BGP() {
		println(l)
	}
}

// discovery handles disc command
func discovery() {
	var (
		wg sync.WaitGroup
		//ts = time.Now()
	)

	d := disc.New(args)
	// help requested
	if d == nil {
		return
	}

	spin.Prefix = "please wait "
	spin.Start()

	// load OUI async
	go func() {
		wg.Add(1)
		d.LoadOUI()
		wg.Done()
	}()
	d.PingLan()
	//s := 10 - int(time.Since(ts).Seconds())
	time.Sleep(5 * time.Second)
	if err := d.GetARPTable(); err != nil {
		println(err.Error())
		return
	}
	wg.Wait()
	spin.Stop()

	println("Network LAN Discovery")
	d.PrintPretty()
}

// setConfig
func setConfig() {
	cli.SetConfig(args, &cfg)
}

// showConfig
func show() {
	if args == "config" {
		cli.ShowConfig(&cfg)
	}
}

// setLG set lg prompt and completer
func setLG() {
	c.SetPrompt("lg")
	c.UpdateCompleter("connect", pNames)
}

// setNS set ns prompt and update completers
func setNS() {
	c.UpdateCompleter("connect", nsr.CountryList())
	c.UpdateCompleter("node", []string{})
	c.SetPrompt("ns")
}

// peeringDB gets peer info
func peeringDB() {
	peeringdb.Search(args)
}

// whoisLookup gets ANS/Prefix info
func whoisLookup() {
	whois.Lookup(args)
}

// local set prompts to local
func local() {
	nsr.Local()
	cPName = "local"
	c.SetPrompt(cPName)
}

// printVersion prints version and exits
func printVersion() {
	fmt.Printf("myLG v%s\n", version)
}

// cleanUp
func cleanUp() {
	c.Close(nxt)
	close(req)
}

// help
func help() {
	if noIf {
		// without command line
		h := `
              ***** TRY IT WITHOUT ANYTHING TO HAVE INTERFACE *****
        Usage:
              mylg [command] [args...]

              Available commands:

              ping                        ping ip address or domain name
              trace                       trace ip address or domain name (real-time w/ -r option)
              dig                         name server looking up
              whois                       resolve AS number/IP/CIDR to holder (provides by ripe ncc)
              hping                       Ping through HTTP/HTTPS w/ GET/HEAD methods
              scan                        scan tcp ports (you can provide range >scan host minport maxport)
              dump                        prints out a description of the contents of packets on a network interface
              disc                        discover all the devices on a LAN
              peering                     peering information (provides by peeringdb.com)
              version                     shows mylg version

        Example:
              mylg trace freebsd.org -r
              mylg whois 8.8.8.8
              mylg scan 127.0.0.1
              mylg dig google.com +trace
		`
		fmt.Println(h)
	} else {
		// with command line interface

		c.Help()
	}
}
<commit_msg>fix minor bugs, init nms<commit_after>// myLG is command line looking glass that written with Go language
// it tries from its own icmp and external looking glasses tools
package main

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/briandowns/spinner"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/disc"
	"github.com/mehrdadrad/mylg/http/ping"
	"github.com/mehrdadrad/mylg/icmp"
	"github.com/mehrdadrad/mylg/lg"
	"github.com/mehrdadrad/mylg/nms"
	"github.com/mehrdadrad/mylg/ns"
	"github.com/mehrdadrad/mylg/packet"
	"github.com/mehrdadrad/mylg/peeringdb"
	"github.com/mehrdadrad/mylg/scan"
	"github.com/mehrdadrad/mylg/services/httpd"
	"github.com/mehrdadrad/mylg/whois"
)

const (
	version = "0.2.4"
)

// Provider represents looking glass
type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() []string
	ChangeNode(node string) bool
	Ping() (string, error)
	Trace() chan string
	BGP() chan string
}

var (
	pNames    = providerNames()
	req       = make(chan string, 1)
	nxt       = make(chan struct{}, 1)
	spin      = spinner.New(spinner.CharSets[26], 220*time.Millisecond)
	eArgs     = os.Args
	args      string
	prompt    string
	cPName    string
	noIf      bool = true
	cfg       cli.Config
	nmsClient nms.Client
	nsr       *ns.Request
	c         *cli.Readline

	// register looking glass hosts
	providers = map[string]Provider{
		"telia":  new(lg.Telia),  // telia
		"level3": new(lg.Level3), // level3
		"cogent": new(lg.Cogent), //cogent
	}

	// map cmd to function
	cmdFunc = map[string]func(){
		"web":     web,          // web dashboard
		"dump":    dump,         // dump traffic
		"disc":    discovery,    // network discovery
		"scan":    scanPorts,    // network scan
		"mode":    mode,         // editor mode
		"ping":    pingQuery,    // ping
		"trace":   trace,        // trace route
		"bgp":     BGP,          // BGP
		"whois":   whoisLookup,  // whois / dns lookup
		"peering": peeringDB,    // peering DB
		"hping":   hping,        // hping
		"dig":     dig,          // dig
		"nms":     setNMS,       // network management system
		"node":    node,         // change node
		"connect": connect,      // connect to a country or LG
		"local":   local,        // local
		"help":    help,         // help
		"exit":    cleanUp,      // clean up
		"quit":    cleanUp,      // clean up
		"show":    show,         // show config
		"set":     setConfig,    // set config
		"lg":      setLG,        // prepare looking glass
		"ns":      setNS,        // prepare name server
		"version": printVersion, // prints version
	}
)

// init
func init() {
	// load configuration
	cfg = cli.LoadConfig()
	// initialize name server
	nsr = ns.NewRequest()
	go nsr.Init()
	// set current provider, prompt
	cPName = "local"
	prompt = "local"
	// with interface
	if len(eArgs) == 1 {
		// initialize cli
		c = cli.Init(version)
		go c.Run(req, nxt)
		// start web server
		go httpd.Run(cfg)
		// set interface enabled
		noIf = false
		// set local as default
		local()
	}

}

func main() {
	// command line w/o interface
	if noIf {
		cmd := eArgs[1]
		args = strings.Join(eArgs[2:], " ")
		if f, ok := cmdFunc[cmd]; ok {
			f()
		} else {
			println("Invalid command please try mylg help")
		}
		return
	}
	// command like w/ interface
LOOP:
	for {
		select {
		case request, ok := <-req:
			if !ok {
				break LOOP
			}
			if len(request) < 1 {
				c.Next()
				continue
			}
			subReq := cli.CMDRgx().FindStringSubmatch(request)
			if len(subReq) == 0 {
				println("syntax error")
				c.Next()
				continue
			}
			prompt = c.GetPrompt()
			args = strings.TrimSpace(subReq[2])
			cmd := strings.TrimSpace(subReq[1])
			if f, ok := cmdFunc[cmd]; ok {
				f()
			} else {
				println("Invalid command please try help")
			}
			c.Next()
		}
	}
}

// providerName
func providerNames() []string {
	pNames := []string{}
	for p := range providers {
		pNames = append(pNames, p)
	}
	return pNames
}

// validateProvider
func validateProvider(p string) (string, error) {
	pNames := []string{}
	match, _ := regexp.MatchString("("+strings.Join(pNames, "|")+")", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	}
	return "", errors.New("provider not support")

}

// node handles node cmd
func node() {
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if _, ok := providers[cPName]; ok {
			if providers[cPName].ChangeNode(args) {
				c.UpdatePromptN(args, 3)
				return
			}
		}
		println("the specified node doesn't support")
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkNode(args) {
			println("error: argument is not valid")
		} else {
			c.UpdatePromptN(args, 3)
		}
	default:
		if cPName == "local" {
			println("local doesn't support node")
		}

	}
}

// dig gets dig info
func dig() {
	if ok := nsr.SetOptions(args, prompt); ok {
		nsr.Dig()
	}
}

// web tries to open web interface at default web browser
func web() {
	var openCmd = "open"
	println("opening default web broswer ...")
	if runtime.GOOS != "darwin" {
		openCmd = "xdg-open"
	}
	cmd := exec.Command(openCmd, fmt.Sprintf("http://%s:%d", cfg.Web.Address, cfg.Web.Port))
	err := cmd.Start()
	if err != nil {
		println("error opening default browser")
	}

}

// dump provides decoding packets
func dump() {
	p, err := packet.NewPacket(args)
	if p == nil || err != nil {
		return
	}
	println(p.Banner())
	for l := range p.Open() {
		l.PrintPretty()
	}
}

// connect handles connect cmd
func connect() {
	var (
		pName string
		err   error
	)
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if pName, err = validateProvider(args); err != nil {
			println("provider not available")
			c.Next()
			return
		}
		cPName = pName
		if _, ok := providers[cPName]; ok {
			c.UpdatePromptN(cPName+"/"+providers[cPName].GetDefaultNode(), 2)
			go func() {
				c.UpdateCompleter("node", providers[cPName].GetNodes())
			}()
		} else {
			println("it doesn't support")
		}
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkCountry(args) {
			println("error: argument is not valid")
		} else {
			c.SetPrompt("ns/" + args)
			c.UpdateCompleter("node", nsr.NodeList())
		}

	case strings.HasPrefix(prompt, "nms"):
		nmsClient, err = nms.NewClient(args, cfg)
		if err != nil {
			println("error:", err.Error())
		} else if nmsClient.Host == "" {
			return
		} else {
			c.SetPrompt("nms/" + nmsClient.Host)
		}
	}
}

// mode set editor mode
func mode() {
	if args == "vim" {
		c.SetVim()
	} else if args == "emacs" {
		c.SetEmacs()
	} else {
		println("the request mode doesn't support")
	}
}

// trace tries to trace from local and lg
func trace() {
	switch {
	case strings.HasPrefix(prompt, "local"):
		trace, err := icmp.NewTrace(args, cfg)
		if err != nil {
			println(err.Error())
		}
		if trace == nil {
			break
		}
		trace.Print()
	case strings.HasPrefix(prompt, "lg"):
		providers[cPName].Set(args, "ipv4")
		for l := range providers[cPName].Trace() {
			println(l)
		}
	}
}

// hping tries to ping a web server by http
func hping() {
	// it should work at local mode
	if cPName != "local" {
		return
	}
	p, err := ping.NewPing(args, cfg)
	if err != nil {
		println(err.Error())
	} else {
		p.Run()
	}
}

// pingQuery runs ping command (local/LG)
func pingQuery() {
	if cPName == "local" {
		pingLocal()
	} else {
		pingLG()
	}
}

// pingLG tries to ping through a looking glass
func pingLG() {
	spin.Prefix = "please wait "
	spin.Start()
	providers[cPName].Set(args, "ipv4")
	m, err := providers[cPName].Ping()
	spin.Stop()
	if err != nil {
		println(err.Error())
	} else {
		println(m)
	}
}

// pingLocal tries to ping from local source ip
func pingLocal() {
	p, err := icmp.NewPing(args, cfg)
	if err != nil {
		println(err.Error())
	}
	if p == nil {
		return
	}
	if !p.IsCIDR() {
		resp := p.Run()
		p.PrintPretty(resp)
	}
}

// scanPorts tries to scan tcp/ip ports
func scanPorts() {
	scan, err := scan.NewScan(args, cfg)
	if err != nil {
		println(err.Error())
	} else {
		scan.Run()
	}
}

// BGP tries to get BGP lookup from a LG
func BGP() {
	if cPName == "local" {
		println("no provider selected")
		return
	}
	providers[cPName].Set(args, "ipv4")
	for l := range providers[cPName].BGP() {
		println(l)
	}
}

// discovery handles disc command
func discovery() {
	var (
		wg sync.WaitGroup
		//ts = time.Now()
	)

	d := disc.New(args)
	// help requested
	if d == nil {
		return
	}

	spin.Prefix = "please wait "
	spin.Start()

	// load OUI async
	go func() {
		wg.Add(1)
		d.LoadOUI()
		wg.Done()
	}()
	d.PingLan()
	//s := 10 - int(time.Since(ts).Seconds())
	time.Sleep(5 * time.Second)
	if err := d.GetARPTable(); err != nil {
		println(err.Error())
		return
	}
	wg.Wait()
	spin.Stop()

	println("Network LAN Discovery")
	d.PrintPretty()
}

// nms
func setNMS() {
	items := []string{"interface", "config"}
	c.UpdateCompleter("show", items)
	c.SetPrompt("nms")
}

// setConfig
func setConfig() {
	cli.SetConfig(args, &cfg)
}

// show command
func show() {
	switch args {
	case "config":
		cli.ShowConfig(&cfg)
	case "interface":
		nmsClient.ShowInterface()
	}
}

// setLG set lg prompt and completer
func setLG() {
	c.SetPrompt("lg")
	c.UpdateCompleter("connect", pNames)
}

// setNS set ns prompt and update completers
func setNS() {
	c.UpdateCompleter("connect", nsr.CountryList())
	c.UpdateCompleter("node", []string{})
	c.SetPrompt("ns")
}

// peeringDB gets peer info
func peeringDB() {
	peeringdb.Search(args)
}

// whoisLookup gets ANS/Prefix info
func whoisLookup() {
	whois.Lookup(args)
}

// local set prompts to local
func local() {
	nsr.Local()
	cPName = "local"
	c.UpdateCompleter("show", []string{"config"})
	c.SetPrompt(cPName)
}

// printVersion prints version and exits
func printVersion() {
	fmt.Printf("myLG v%s\n", version)
}

// cleanUp
func cleanUp() {
	c.Close(nxt)
	close(req)
}

// help
func help() {
	if noIf {
		// without command line
		h := `
              ***** TRY IT WITHOUT ANYTHING TO HAVE INTERFACE *****
        Usage:
              mylg [command] [args...]

              Available commands:

              ping                        ping ip address or domain name
              trace                       trace ip address or domain name (real-time w/ -r option)
              dig                         name server looking up
              whois                       resolve AS number/IP/CIDR to holder (provides by ripe ncc)
              hping                       Ping through HTTP/HTTPS w/ GET/HEAD methods
              scan                        scan tcp ports (you can provide range >scan host minport maxport)
              dump                        prints out a description of the contents of packets on a network interface
              disc                        discover all the devices on a LAN
              peering                     peering information (provides by peeringdb.com)
              version                     shows mylg version

        Example:
              mylg trace freebsd.org -r
              mylg whois 8.8.8.8
              mylg scan 127.0.0.1
              mylg dig google.com +trace
		`
		fmt.Println(h)
	} else {
		// with command line interface

		c.Help()
	}
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd Authors.

   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 cni

import (
	"sort"
	"strings"

	cnilibrary "github.com/containernetworking/cni/libcni"
	"github.com/pkg/errors"
)

type CNIOpt func(c *libcni) error

// WithInterfacePrefix sets the prefix for network interfaces
// e.g. eth or wlan
func WithInterfacePrefix(prefix string) CNIOpt {
	return func(c *libcni) error {
		c.prefix = prefix
		return nil
	}
}

// WithPluginDir can be used to set the locations of
// the cni plugin binaries
func WithPluginDir(dirs []string) CNIOpt {
	return func(c *libcni) error {
		c.pluginDirs = dirs
		c.cniConfig = &cnilibrary.CNIConfig{Path: dirs}
		return nil
	}
}

// WithPluginConfDir can be used to configure the
// cni configuration directory.
func WithPluginConfDir(dir string) CNIOpt {
	return func(c *libcni) error {
		c.pluginConfDir = dir
		return nil
	}
}

// WithPluginMaxConfNum can be used to configure the
// max cni plugin config file num.
func WithPluginMaxConfNum(max int) CNIOpt {
	return func(c *libcni) error {
		c.pluginMaxConfNum = max
		return nil
	}
}

// WithMinNetworkCount can be used to configure the
// minimum networks to be configured and initialized
// for the status to report success. By default its 1.
func WithMinNetworkCount(count int) CNIOpt {
	return func(c *libcni) error {
		c.networkCount = count
		return nil
	}
}

// WithLoNetwork can be used to load the loopback
// network config.
func WithLoNetwork(c *libcni) error {
	loConfig, _ := cnilibrary.ConfListFromBytes([]byte(`{
"cniVersion": "0.3.1",
"name": "cni-loopback",
"plugins": [{
  "type": "loopback"
}]
}`))

	c.networks = append(c.networks, &Network{
		cni:    c.cniConfig,
		config: loConfig,
		ifName: "lo",
	})
	return nil
}

// WithConf can be used to load config directly
// from byte.
func WithConf(bytes []byte) CNIOpt {
	return WithConfIndex(bytes, 0)
}

// WithConfIndex can be used to load config directly
// from byte and set the interface name's index.
func WithConfIndex(bytes []byte, index int) CNIOpt {
	return func(c *libcni) error {
		conf, err := cnilibrary.ConfFromBytes(bytes)
		if err != nil {
			return err
		}
		confList, err := cnilibrary.ConfListFromConf(conf)
		if err != nil {
			return err
		}
		c.networks = append(c.networks, &Network{
			cni:    c.cniConfig,
			config: confList,
			ifName: getIfName(c.prefix, index),
		})
		return nil
	}
}

// WithConfFile can be used to load network config
// from an .conf file. Supported with absolute fileName
// with path only.
func WithConfFile(fileName string) CNIOpt {
	return func(c *libcni) error {
		conf, err := cnilibrary.ConfFromFile(fileName)
		if err != nil {
			return err
		}
		// upconvert to conf list
		confList, err := cnilibrary.ConfListFromConf(conf)
		if err != nil {
			return err
		}
		c.networks = append(c.networks, &Network{
			cni:    c.cniConfig,
			config: confList,
			ifName: getIfName(c.prefix, 0),
		})
		return nil
	}
}

// WithConfListFile can be used to load network config
// from an .conflist file. Supported with absolute fileName
// with path only.
func WithConfListFile(fileName string) CNIOpt {
	return func(c *libcni) error {
		confList, err := cnilibrary.ConfListFromFile(fileName)
		if err != nil {
			return err
		}
		i := len(c.networks)
		c.networks = append(c.networks, &Network{
			cni:    c.cniConfig,
			config: confList,
			ifName: getIfName(c.prefix, i),
		})
		return nil
	}
}

// WithDefaultConf can be used to detect the default network
// config file from the configured cni config directory and load
// it.
// Since the CNI spec does not specify a way to detect default networks,
// the convention chosen is - the first network configuration in the sorted
// list of network conf files as the default network.
func WithDefaultConf(c *libcni) error {
	return loadFromConfDir(c, c.pluginMaxConfNum)
}

// WithAllConf can be used to detect all network config
// files from the configured cni config directory and load
// them.
func WithAllConf(c *libcni) error {
	return loadFromConfDir(c, 0)
}

// loadFromConfDir detects network config files from the
// configured cni config directory and load them. max is
// the maximum network config to load (max i<= 0 means no limit).
func loadFromConfDir(c *libcni, max int) error {
	files, err := cnilibrary.ConfFiles(c.pluginConfDir, []string{".conf", ".conflist", ".json"})
	switch {
	case err != nil:
		return errors.Wrapf(ErrRead, "failed to read config file: %v", err)
	case len(files) == 0:
		return errors.Wrapf(ErrCNINotInitialized, "no network config found in %s", c.pluginConfDir)
	}

	// files contains the network config files associated with cni network.
	// Use lexicographical way as a defined order for network config files.
	sort.Strings(files)
	// Since the CNI spec does not specify a way to detect default networks,
	// the convention chosen is - the first network configuration in the sorted
	// list of network conf files as the default network and choose the default
	// interface provided during init as the network interface for this default
	// network. For every other network use a generated interface id.
	i := 0
	var networks []*Network
	for _, confFile := range files {
		var confList *cnilibrary.NetworkConfigList
		if strings.HasSuffix(confFile, ".conflist") {
			confList, err = cnilibrary.ConfListFromFile(confFile)
			if err != nil {
				return errors.Wrapf(ErrInvalidConfig, "failed to load CNI config list file %s: %v", confFile, err)
			}
		} else {
			conf, err := cnilibrary.ConfFromFile(confFile)
			if err != nil {
				return errors.Wrapf(ErrInvalidConfig, "failed to load CNI config file %s: %v", confFile, err)
			}
			// Ensure the config has a "type" so we know what plugin to run.
			// Also catches the case where somebody put a conflist into a conf file.
			if conf.Network.Type == "" {
				return errors.Wrapf(ErrInvalidConfig, "network type not found in %s", confFile)
			}

			confList, err = cnilibrary.ConfListFromConf(conf)
			if err != nil {
				return errors.Wrapf(ErrInvalidConfig, "failed to convert CNI config file %s to CNI config list: %v", confFile, err)
			}
		}
		if len(confList.Plugins) == 0 {
			return errors.Wrapf(ErrInvalidConfig, "CNI config list in config file %s has no networks, skipping", confFile)

		}
		networks = append(networks, &Network{
			cni:    c.cniConfig,
			config: confList,
			ifName: getIfName(c.prefix, i),
		})
		i++
		if i == max {
			break
		}
	}
	if len(networks) == 0 {
		return errors.Wrapf(ErrCNINotInitialized, "no valid networks found in %s", c.pluginDirs)
	}
	c.networks = append(c.networks, networks...)
	return nil
}
<commit_msg>add WithConfList opt for adding conflist from byte<commit_after>/*
   Copyright The containerd Authors.

   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 cni

import (
	"sort"
	"strings"

	cnilibrary "github.com/containernetworking/cni/libcni"
	"github.com/pkg/errors"
)

type CNIOpt func(c *libcni) error

// WithInterfacePrefix sets the prefix for network interfaces
// e.g. eth or wlan
func WithInterfacePrefix(prefix string) CNIOpt {
	return func(c *libcni) error {
		c.prefix = prefix
		return nil
	}
}

// WithPluginDir can be used to set the locations of
// the cni plugin binaries
func WithPluginDir(dirs []string) CNIOpt {
	return func(c *libcni) error {
		c.pluginDirs = dirs
		c.cniConfig = &cnilibrary.CNIConfig{Path: dirs}
		return nil
	}
}

// WithPluginConfDir can be used to configure the
// cni configuration directory.
func WithPluginConfDir(dir string) CNIOpt {
	return func(c *libcni) error {
		c.pluginConfDir = dir
		return nil
	}
}

// WithPluginMaxConfNum can be used to configure the
// max cni plugin config file num.
func WithPluginMaxConfNum(max int) CNIOpt {
	return func(c *libcni) error {
		c.pluginMaxConfNum = max
		return nil
	}
}

// WithMinNetworkCount can be used to configure the
// minimum networks to be configured and initialized
// for the status to report success. By default its 1.
func WithMinNetworkCount(count int) CNIOpt {
	return func(c *libcni) error {
		c.networkCount = count
		return nil
	}
}

// WithLoNetwork can be used to load the loopback
// network config.
func WithLoNetwork(c *libcni) error {
	loConfig, _ := cnilibrary.ConfListFromBytes([]byte(`{
"cniVersion": "0.3.1",
"name": "cni-loopback",
"plugins": [{
  "type": "loopback"
}]
}`))

	c.networks = append(c.networks, &Network{
		cni:    c.cniConfig,
		config: loConfig,
		ifName: "lo",
	})
	return nil
}

// WithConf can be used to load config directly
// from byte.
func WithConf(bytes []byte) CNIOpt {
	return WithConfIndex(bytes, 0)
}

// WithConfIndex can be used to load config directly
// from byte and set the interface name's index.
func WithConfIndex(bytes []byte, index int) CNIOpt {
	return func(c *libcni) error {
		conf, err := cnilibrary.ConfFromBytes(bytes)
		if err != nil {
			return err
		}
		confList, err := cnilibrary.ConfListFromConf(conf)
		if err != nil {
			return err
		}
		c.networks = append(c.networks, &Network{
			cni:    c.cniConfig,
			config: confList,
			ifName: getIfName(c.prefix, index),
		})
		return nil
	}
}

// WithConfFile can be used to load network config
// from an .conf file. Supported with absolute fileName
// with path only.
func WithConfFile(fileName string) CNIOpt {
	return func(c *libcni) error {
		conf, err := cnilibrary.ConfFromFile(fileName)
		if err != nil {
			return err
		}
		// upconvert to conf list
		confList, err := cnilibrary.ConfListFromConf(conf)
		if err != nil {
			return err
		}
		c.networks = append(c.networks, &Network{
			cni:    c.cniConfig,
			config: confList,
			ifName: getIfName(c.prefix, 0),
		})
		return nil
	}
}

// WithConfListBytes can be used to load network config list directly
// from byte
func WithConfListBytes(bytes []byte) CNIOpt {
	return func(c *libcni) error {
		confList, err := cnilibrary.ConfListFromBytes(bytes)
		if err != nil {
			return err
		}
		i := len(c.networks)
		c.networks = append(c.networks, &Network{
			cni:    c.cniConfig,
			config: confList,
			ifName: getIfName(c.prefix, i),
		})
		return nil
	}
}

// WithConfListFile can be used to load network config
// from an .conflist file. Supported with absolute fileName
// with path only.
func WithConfListFile(fileName string) CNIOpt {
	return func(c *libcni) error {
		confList, err := cnilibrary.ConfListFromFile(fileName)
		if err != nil {
			return err
		}
		i := len(c.networks)
		c.networks = append(c.networks, &Network{
			cni:    c.cniConfig,
			config: confList,
			ifName: getIfName(c.prefix, i),
		})
		return nil
	}
}

// WithDefaultConf can be used to detect the default network
// config file from the configured cni config directory and load
// it.
// Since the CNI spec does not specify a way to detect default networks,
// the convention chosen is - the first network configuration in the sorted
// list of network conf files as the default network.
func WithDefaultConf(c *libcni) error {
	return loadFromConfDir(c, c.pluginMaxConfNum)
}

// WithAllConf can be used to detect all network config
// files from the configured cni config directory and load
// them.
func WithAllConf(c *libcni) error {
	return loadFromConfDir(c, 0)
}

// loadFromConfDir detects network config files from the
// configured cni config directory and load them. max is
// the maximum network config to load (max i<= 0 means no limit).
func loadFromConfDir(c *libcni, max int) error {
	files, err := cnilibrary.ConfFiles(c.pluginConfDir, []string{".conf", ".conflist", ".json"})
	switch {
	case err != nil:
		return errors.Wrapf(ErrRead, "failed to read config file: %v", err)
	case len(files) == 0:
		return errors.Wrapf(ErrCNINotInitialized, "no network config found in %s", c.pluginConfDir)
	}

	// files contains the network config files associated with cni network.
	// Use lexicographical way as a defined order for network config files.
	sort.Strings(files)
	// Since the CNI spec does not specify a way to detect default networks,
	// the convention chosen is - the first network configuration in the sorted
	// list of network conf files as the default network and choose the default
	// interface provided during init as the network interface for this default
	// network. For every other network use a generated interface id.
	i := 0
	var networks []*Network
	for _, confFile := range files {
		var confList *cnilibrary.NetworkConfigList
		if strings.HasSuffix(confFile, ".conflist") {
			confList, err = cnilibrary.ConfListFromFile(confFile)
			if err != nil {
				return errors.Wrapf(ErrInvalidConfig, "failed to load CNI config list file %s: %v", confFile, err)
			}
		} else {
			conf, err := cnilibrary.ConfFromFile(confFile)
			if err != nil {
				return errors.Wrapf(ErrInvalidConfig, "failed to load CNI config file %s: %v", confFile, err)
			}
			// Ensure the config has a "type" so we know what plugin to run.
			// Also catches the case where somebody put a conflist into a conf file.
			if conf.Network.Type == "" {
				return errors.Wrapf(ErrInvalidConfig, "network type not found in %s", confFile)
			}

			confList, err = cnilibrary.ConfListFromConf(conf)
			if err != nil {
				return errors.Wrapf(ErrInvalidConfig, "failed to convert CNI config file %s to CNI config list: %v", confFile, err)
			}
		}
		if len(confList.Plugins) == 0 {
			return errors.Wrapf(ErrInvalidConfig, "CNI config list in config file %s has no networks, skipping", confFile)

		}
		networks = append(networks, &Network{
			cni:    c.cniConfig,
			config: confList,
			ifName: getIfName(c.prefix, i),
		})
		i++
		if i == max {
			break
		}
	}
	if len(networks) == 0 {
		return errors.Wrapf(ErrCNINotInitialized, "no valid networks found in %s", c.pluginDirs)
	}
	c.networks = append(c.networks, networks...)
	return nil
}
<|endoftext|>