<commit_before>package main

import (
	"flag"

	executorinit "github.com/cloudfoundry-incubator/executor/initializer"
)

var gardenNetwork = flag.String(
	"gardenNetwork",
	executorinit.DefaultConfiguration.GardenNetwork,
	"network mode for garden server (tcp, unix)",
)

var gardenAddr = flag.String(
	"gardenAddr",
	executorinit.DefaultConfiguration.GardenAddr,
	"network address for garden server",
)

var memoryMBFlag = flag.String(
	"memoryMB",
	executorinit.DefaultConfiguration.MemoryMB,
	"the amount of memory the executor has available in megabytes",
)

var diskMBFlag = flag.String(
	"diskMB",
	executorinit.DefaultConfiguration.DiskMB,
	"the amount of disk the executor has available in megabytes",
)

var tempDir = flag.String(
	"tempDir",
	executorinit.DefaultConfiguration.TempDir,
	"location to store temporary assets",
)

var registryPruningInterval = flag.Duration(
	"pruneInterval",
	executorinit.DefaultConfiguration.RegistryPruningInterval,
	"amount of time during which a container can remain in the allocated state",
)

var containerInodeLimit = flag.Uint64(
	"containerInodeLimit",
	executorinit.DefaultConfiguration.ContainerInodeLimit,
	"max number of inodes per container",
)

var containerMaxCpuShares = flag.Uint64(
	"containerMaxCpuShares",
	executorinit.DefaultConfiguration.ContainerMaxCpuShares,
	"cpu shares allocatable to a container",
)

var cachePath = flag.String(
	"cachePath",
	executorinit.DefaultConfiguration.CachePath,
	"location to cache assets",
)

var maxCacheSizeInBytes = flag.Uint64(
	"maxCacheSizeInBytes",
	executorinit.DefaultConfiguration.MaxCacheSizeInBytes,
	"maximum size of the cache (in bytes) - you should include a healthy amount of overhead",
)

var skipCertVerify = flag.Bool(
	"skipCertVerify",
	executorinit.DefaultConfiguration.SkipCertVerify,
	"skip SSL certificate verification",
)

var healthyMonitoringInterval = flag.Duration(
	"healthyMonitoringInterval",
	executorinit.DefaultConfiguration.HealthyMonitoringInterval,
	"interval on which to check healthy containers",
)

var unhealthyMonitoringInterval = flag.Duration(
	"unhealthyMonitoringInterval",
	executorinit.DefaultConfiguration.UnhealthyMonitoringInterval,
	"interval on which to check unhealthy containers",
)

var exportNetworkEnvVars = flag.Bool(
	"exportNetworkEnvVars",
	executorinit.DefaultConfiguration.ExportNetworkEnvVars,
	"export network environment variables into container (e.g. CF_INSTANCE_IP, CF_INSTANCE_PORT)",
)

var containerOwnerName = flag.String(
	"containerOwnerName",
	executorinit.DefaultConfiguration.ContainerOwnerName,
	"owner name with which to tag containers",
)

var createWorkPoolSize = flag.Int(
	"createWorkPoolSize",
	executorinit.DefaultConfiguration.CreateWorkPoolSize,
	"Number of concurrent create operations in garden",
)

var deleteWorkPoolSize = flag.Int(
	"deleteWorkPoolSize",
	executorinit.DefaultConfiguration.DeleteWorkPoolSize,
	"Number of concurrent delete operations in garden",
)

var readWorkPoolSize = flag.Int(
	"readWorkPoolSize",
	executorinit.DefaultConfiguration.ReadWorkPoolSize,
	"Number of concurrent read operations in garden",
)

var metricsWorkPoolSize = flag.Int(
	"metricsWorkPoolSize",
	executorinit.DefaultConfiguration.MetricsWorkPoolSize,
	"Number of concurrent metrics operations in garden",
)

var healthCheckWorkPoolSize = flag.Int(
	"healthCheckWorkPoolSize",
	executorinit.DefaultConfiguration.HealthCheckWorkPoolSize,
	"Number of concurrent ping operations in garden",
)

func executorConfig() executorinit.Configuration {
	return executorinit.Configuration{
		GardenNetwork:               *gardenNetwork,
		GardenAddr:                  *gardenAddr,
		ContainerOwnerName:          *containerOwnerName,
		TempDir:                     *tempDir,
		CachePath:                   *cachePath,
		MaxCacheSizeInBytes:         *maxCacheSizeInBytes,
		SkipCertVerify:              *skipCertVerify,
		ExportNetworkEnvVars:        *exportNetworkEnvVars,
		ContainerMaxCpuShares:       *containerMaxCpuShares,
		ContainerInodeLimit:         *containerInodeLimit,
		HealthyMonitoringInterval:   *healthyMonitoringInterval,
		UnhealthyMonitoringInterval: *unhealthyMonitoringInterval,
		HealthCheckWorkPoolSize:     *healthCheckWorkPoolSize,
		CreateWorkPoolSize:          *createWorkPoolSize,
		DeleteWorkPoolSize:          *deleteWorkPoolSize,
		ReadWorkPoolSize:            *readWorkPoolSize,
		MetricsWorkPoolSize:         *metricsWorkPoolSize,
		RegistryPruningInterval:     *registryPruningInterval,
		MemoryMB:                    *memoryMBFlag,
		DiskMB:                      *diskMBFlag,
	}
}
<commit_msg>Expose MaxConcurrentDownloads as a flag<commit_after>package main

import (
	"flag"

	executorinit "github.com/cloudfoundry-incubator/executor/initializer"
)

var gardenNetwork = flag.String(
	"gardenNetwork",
	executorinit.DefaultConfiguration.GardenNetwork,
	"network mode for garden server (tcp, unix)",
)

var gardenAddr = flag.String(
	"gardenAddr",
	executorinit.DefaultConfiguration.GardenAddr,
	"network address for garden server",
)

var memoryMBFlag = flag.String(
	"memoryMB",
	executorinit.DefaultConfiguration.MemoryMB,
	"the amount of memory the executor has available in megabytes",
)

var diskMBFlag = flag.String(
	"diskMB",
	executorinit.DefaultConfiguration.DiskMB,
	"the amount of disk the executor has available in megabytes",
)

var tempDir = flag.String(
	"tempDir",
	executorinit.DefaultConfiguration.TempDir,
	"location to store temporary assets",
)

var registryPruningInterval = flag.Duration(
	"pruneInterval",
	executorinit.DefaultConfiguration.RegistryPruningInterval,
	"amount of time during which a container can remain in the allocated state",
)

var containerInodeLimit = flag.Uint64(
	"containerInodeLimit",
	executorinit.DefaultConfiguration.ContainerInodeLimit,
	"max number of inodes per container",
)

var containerMaxCpuShares = flag.Uint64(
	"containerMaxCpuShares",
	executorinit.DefaultConfiguration.ContainerMaxCpuShares,
	"cpu shares allocatable to a container",
)

var cachePath = flag.String(
	"cachePath",
	executorinit.DefaultConfiguration.CachePath,
	"location to cache assets",
)

var maxCacheSizeInBytes = flag.Uint64(
	"maxCacheSizeInBytes",
	executorinit.DefaultConfiguration.MaxCacheSizeInBytes,
	"maximum size of the cache (in bytes) - you should include a healthy amount of overhead",
)

var skipCertVerify = flag.Bool(
	"skipCertVerify",
	executorinit.DefaultConfiguration.SkipCertVerify,
	"skip SSL certificate verification",
)

var healthyMonitoringInterval = flag.Duration(
	"healthyMonitoringInterval",
	executorinit.DefaultConfiguration.HealthyMonitoringInterval,
	"interval on which to check healthy containers",
)

var unhealthyMonitoringInterval = flag.Duration(
	"unhealthyMonitoringInterval",
	executorinit.DefaultConfiguration.UnhealthyMonitoringInterval,
	"interval on which to check unhealthy containers",
)

var exportNetworkEnvVars = flag.Bool(
	"exportNetworkEnvVars",
	executorinit.DefaultConfiguration.ExportNetworkEnvVars,
	"export network environment variables into container (e.g. CF_INSTANCE_IP, CF_INSTANCE_PORT)",
)

var containerOwnerName = flag.String(
	"containerOwnerName",
	executorinit.DefaultConfiguration.ContainerOwnerName,
	"owner name with which to tag containers",
)

var createWorkPoolSize = flag.Int(
	"createWorkPoolSize",
	executorinit.DefaultConfiguration.CreateWorkPoolSize,
	"Number of concurrent create operations in garden",
)

var deleteWorkPoolSize = flag.Int(
	"deleteWorkPoolSize",
	executorinit.DefaultConfiguration.DeleteWorkPoolSize,
	"Number of concurrent delete operations in garden",
)

var readWorkPoolSize = flag.Int(
	"readWorkPoolSize",
	executorinit.DefaultConfiguration.ReadWorkPoolSize,
	"Number of concurrent read operations in garden",
)

var metricsWorkPoolSize = flag.Int(
	"metricsWorkPoolSize",
	executorinit.DefaultConfiguration.MetricsWorkPoolSize,
	"Number of concurrent metrics operations in garden",
)

var healthCheckWorkPoolSize = flag.Int(
	"healthCheckWorkPoolSize",
	executorinit.DefaultConfiguration.HealthCheckWorkPoolSize,
	"Number of concurrent ping operations in garden",
)

var maxConcurrentDownloads = flag.Int(
	"maxConcurrentDownloads",
	executorinit.DefaultConfiguration.MaxConcurrentDownloads,
	"Number of concurrent download steps",
)

func executorConfig() executorinit.Configuration {
	return executorinit.Configuration{
		GardenNetwork:               *gardenNetwork,
		GardenAddr:                  *gardenAddr,
		ContainerOwnerName:          *containerOwnerName,
		TempDir:                     *tempDir,
		CachePath:                   *cachePath,
		MaxCacheSizeInBytes:         *maxCacheSizeInBytes,
		SkipCertVerify:              *skipCertVerify,
		ExportNetworkEnvVars:        *exportNetworkEnvVars,
		ContainerMaxCpuShares:       *containerMaxCpuShares,
		ContainerInodeLimit:         *containerInodeLimit,
		HealthyMonitoringInterval:   *healthyMonitoringInterval,
		UnhealthyMonitoringInterval: *unhealthyMonitoringInterval,
		HealthCheckWorkPoolSize:     *healthCheckWorkPoolSize,
		CreateWorkPoolSize:          *createWorkPoolSize,
		DeleteWorkPoolSize:          *deleteWorkPoolSize,
		ReadWorkPoolSize:            *readWorkPoolSize,
		MetricsWorkPoolSize:         *metricsWorkPoolSize,
		RegistryPruningInterval:     *registryPruningInterval,
		MemoryMB:                    *memoryMBFlag,
		DiskMB:                      *diskMBFlag,
		MaxConcurrentDownloads:      *maxConcurrentDownloads,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/drbig/ricons"
)

const (
	VERSION = `0.0.1`
)

type format struct {
	mime string
	fmt  ricons.Format
}

type Info struct {
	Generators map[string]string `json:"generators"`
	Formats    []string          `json:"formats"`
	Versions   map[string]string `json:"versions"`
}

var (
	fAddr  string
	fGens  bool
	fBound int
	fQuiet bool
	gens   map[string]string
	fmts   map[string]format
	info   []byte
)

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options]\n", os.Args[0])
		flag.PrintDefaults()
	}
	flag.StringVar(&fAddr, "a", ":3232", "server bind address")
	flag.BoolVar(&fGens, "l", false, "show generators and exit")
	flag.IntVar(&fBound, "b", 256, "image bound (e.g. max 256x256)")
	flag.BoolVar(&fQuiet, "q", false, "disable logging")

	gens = make(map[string]string, len(ricons.Registry))
	for k, v := range ricons.Registry {
		gens[k] = v.String()
	}

	fmts = map[string]format{
		"png":  format{"image/png", ricons.PNG},
		"gif":  format{"image/gif", ricons.GIF},
		"jpeg": format{"image/jpeg", ricons.JPEG},
	}

	is := &Info{
		gens,
		make([]string, len(fmts)),
		map[string]string{
			"ricons":  ricons.VERSION,
			"riconsd": VERSION,
		},
	}
	i := 0
	for k, _ := range fmts {
		is.Formats[i] = k
		i++
	}
	in, err := json.Marshal(is)
	if err != nil {
		panic(fmt.Sprintf("can't marshal info: %s", err))
	}
	info = in
}

func main() {
	flag.Parse()
	if fGens {
		for _, v := range gens {
			fmt.Println(v)
		}
		os.Exit(0)
	}
	if fQuiet {
		log.SetOutput(ioutil.Discard)
	}

	http.HandleFunc("/info.json", handleInfo)
	http.HandleFunc("/", handleIcon)
	log.Println("Starting HTTP server at", fAddr)
	go func() {
		log.Fatalln(http.ListenAndServe(fAddr, nil))
	}()
	sigwait()
}

func handleInfo(w http.ResponseWriter, req *http.Request) {
	log.Println(req.RemoteAddr, req.Method, req.RequestURI)

	w.Header().Set("Content-Type", "application/json")
	w.Write(info)
	return
}

func handleIcon(w http.ResponseWriter, req *http.Request) {
	log.Println(req.RemoteAddr, req.Method, req.RequestURI)

	w.Header().Set("Content-Type", "application/json")
	parts := strings.Split(req.URL.Path, "/")
	if len(parts) != 5 {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"success": false, "msg": "wrong request URI"}`))
		return
	}
	g, ok := ricons.Registry[parts[1]]
	if !ok {
		w.WriteHeader(http.StatusNotImplemented)
		w.Write([]byte(`{"success": false, "msg": "generator not found"}`))
		return
	}
	f, ok := fmts[parts[2]]
	if !ok {
		w.WriteHeader(http.StatusNotImplemented)
		w.Write([]byte(`{"success": false, "msg": "unknown image format"}`))
		return
	}
	wi, err := strconv.Atoi(parts[3])
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"success": false, "msg": "error parsing image width"}`))
		return

	}
	if wi < 1 || wi > fBound {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"success": false, "msg": "image width out of range"}`))
		return
	}
	h, err := strconv.Atoi(parts[4])
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"success": false, "msg": "error parsing image height"}`))
		return
	}
	if h < 1 || h > fBound {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"success": false, "msg": "image height out of range"}`))
		return
	}
	i, err := g.NewIcon(wi, h)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(`{"success": false, "msg": "error generating icon, sorry"}`))
		return
	}
	w.Header().Set("Content-Type", f.mime)
	w.WriteHeader(http.StatusOK)
	i.Encode(f.fmt, w)
	return
}
<commit_msg>cmd/riconsd: Small cleanup<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/drbig/ricons"
)

const (
	VERSION = `0.0.1`
)

type format struct {
	mime string
	fmt  ricons.Format
}

type Info struct {
	Generators map[string]string `json:"generators"`
	Formats    []string          `json:"formats"`
	Versions   map[string]string `json:"versions"`
}

var (
	fAddr  string
	fGens  bool
	fBound int
	fQuiet bool
)

var (
	gens map[string]string
	fmts map[string]format
	info []byte
)

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options]\n", os.Args[0])
		flag.PrintDefaults()
	}
	flag.StringVar(&fAddr, "a", ":3232", "server bind address")
	flag.BoolVar(&fGens, "l", false, "show generators and exit")
	flag.IntVar(&fBound, "b", 256, "image bound (e.g. max 256x256)")
	flag.BoolVar(&fQuiet, "q", false, "disable logging")

	gens = make(map[string]string, len(ricons.Registry))
	for k, v := range ricons.Registry {
		gens[k] = v.String()
	}

	fmts = map[string]format{
		"png":  format{"image/png", ricons.PNG},
		"gif":  format{"image/gif", ricons.GIF},
		"jpeg": format{"image/jpeg", ricons.JPEG},
	}

	is := &Info{
		gens,
		make([]string, len(fmts)),
		map[string]string{
			"ricons":  ricons.VERSION,
			"riconsd": VERSION,
		},
	}
	i := 0
	for k, _ := range fmts {
		is.Formats[i] = k
		i++
	}
	in, err := json.Marshal(is)
	if err != nil {
		panic(fmt.Sprintf("can't marshal info: %s", err))
	}
	info = in
}

func main() {
	flag.Parse()
	if fGens {
		for _, v := range gens {
			fmt.Println(v)
		}
		os.Exit(0)
	}
	if fQuiet {
		log.SetOutput(ioutil.Discard)
	}

	http.HandleFunc("/info.json", handleInfo)
	http.HandleFunc("/", handleIcon)
	log.Println("Starting HTTP server at", fAddr)
	go func() {
		log.Fatalln(http.ListenAndServe(fAddr, nil))
	}()
	sigwait()
}

func handleInfo(w http.ResponseWriter, req *http.Request) {
	log.Println(req.RemoteAddr, req.Method, req.RequestURI)

	w.Header().Set("Content-Type", "application/json")
	w.Write(info)
	return
}

func handleIcon(w http.ResponseWriter, req *http.Request) {
	log.Println(req.RemoteAddr, req.Method, req.RequestURI)

	w.Header().Set("Content-Type", "application/json")
	parts := strings.Split(req.URL.Path, "/")
	if len(parts) != 5 {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"success": false, "msg": "wrong request URI"}`))
		return
	}
	g, ok := ricons.Registry[parts[1]]
	if !ok {
		w.WriteHeader(http.StatusNotImplemented)
		w.Write([]byte(`{"success": false, "msg": "generator not found"}`))
		return
	}
	f, ok := fmts[parts[2]]
	if !ok {
		w.WriteHeader(http.StatusNotImplemented)
		w.Write([]byte(`{"success": false, "msg": "unknown image format"}`))
		return
	}
	wi, err := strconv.Atoi(parts[3])
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"success": false, "msg": "error parsing image width"}`))
		return
	}
	if wi < 1 || wi > fBound {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"success": false, "msg": "image width out of range"}`))
		return
	}
	h, err := strconv.Atoi(parts[4])
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"success": false, "msg": "error parsing image height"}`))
		return
	}
	if h < 1 || h > fBound {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"success": false, "msg": "image height out of range"}`))
		return
	}
	i, err := g.NewIcon(wi, h)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(`{"success": false, "msg": "error generating icon, sorry"}`))
		return
	}
	w.Header().Set("Content-Type", f.mime)
	w.WriteHeader(http.StatusOK)
	i.Encode(f.fmt, w)
	return
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"log"
	"net/http"
	"psychic-rat/api"
)

type variables struct {
	Username string
	Items    []api.ItemElement
}

func renderPage(writer http.ResponseWriter, templateName string, variables interface{}) {
	tpt := template.Must(template.New(templateName).ParseFiles(templateName, "header.html.tmpl", "footer.html.tmpl"))
	//tpt.ExecuteTemplate(writer, "header.html", variables)
	tpt.Execute(writer, variables)
	//tpt.ExecuteTemplate(writer, "footer.html", variables)
}

func HomePageHandler(writer http.ResponseWriter, request *http.Request) {
	vars := variables{Username: "Kevin"}
	renderPage(writer, "home.html.tmpl", vars)
}

func SignInPageHandler(writer http.ResponseWriter, request *http.Request) {
	vars := variables{Username: "Kevin"}
	renderPage(writer, "signin.html.tmpl", vars)
}

func PledgePageHandler(writer http.ResponseWriter, request *http.Request) {
	report, err := itemApi.ListItems()
	if err != nil {
		log.Fatal(err)
	}

	vars := variables{Username: "Kevin", Items: report.Items}
	renderPage(writer, "pledge.html.tmpl", vars)
}

func ThanksPageHandler(writer http.ResponseWriter, request *http.Request) {
	vars := variables{Username: "Kevin"}
	renderPage(writer, "thanks.html.tmpl", vars)
}
<commit_msg>Render<commit_after>package main

import (
	"html/template"
	"log"
	"net/http"
	"psychic-rat/api"
)

type variables struct {
	Username string
	Items    []api.ItemElement
}

func HomePageHandler(writer http.ResponseWriter, request *http.Request) {
	vars := variables{Username: "Kevin"}
	renderPage(writer, "home.html.tmpl", vars)
}

func renderPage(writer http.ResponseWriter, templateName string, variables interface{}) {
	tpt := template.Must(template.New(templateName).ParseFiles(templateName, "header.html.tmpl", "footer.html.tmpl"))
	tpt.Execute(writer, variables)
}

func SignInPageHandler(writer http.ResponseWriter, request *http.Request) {
	vars := variables{Username: "Kevin"}
	renderPage(writer, "signin.html.tmpl", vars)
}

func PledgePageHandler(writer http.ResponseWriter, request *http.Request) {
	report, err := itemApi.ListItems()
	if err != nil {
		log.Fatal(err)
	}

	vars := variables{Username: "Kevin", Items: report.Items}
	renderPage(writer, "pledge.html.tmpl", vars)
}

func ThanksPageHandler(writer http.ResponseWriter, request *http.Request) {
	vars := variables{Username: "Kevin"}
	renderPage(writer, "thanks.html.tmpl", vars)
}
<|endoftext|>


<commit_before>/*
 * Minio Cloud Storage, (C) 2016 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

// Signature and API related constants.
const (
	signV2Algorithm = "AWS"
)

// AWS S3 Signature V2 calculation rule is give here:
// http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationStringToSign

// Whitelist resource list that will be used in query string for signature-V2 calculation.
// The list should be alphabetically sorted
var resourceList = []string{
	"acl",
	"delete",
	"lifecycle",
	"location",
	"logging",
	"notification",
	"partNumber",
	"policy",
	"requestPayment",
	"response-cache-control",
	"response-content-disposition",
	"response-content-encoding",
	"response-content-language",
	"response-content-type",
	"response-expires",
	"torrent",
	"uploadId",
	"uploads",
	"versionId",
	"versioning",
	"versions",
	"website",
}

func doesPolicySignatureV2Match(formValues map[string]string) APIErrorCode {
	cred := serverConfig.GetCredential()
	accessKey := formValues["Awsaccesskeyid"]
	if accessKey != cred.AccessKey {
		return ErrInvalidAccessKeyID
	}
	signature := formValues["Signature"]
	policy := formValues["Policy"]
	if signature != calculateSignatureV2(policy, cred.SecretKey) {
		return ErrSignatureDoesNotMatch
	}
	return ErrNone
}

// doesPresignV2SignatureMatch - Verify query headers with presigned signature
//     - http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationQueryStringAuth
// returns ErrNone if matches. S3 errors otherwise.
func doesPresignV2SignatureMatch(r *http.Request) APIErrorCode {
	// Access credentials.
	cred := serverConfig.GetCredential()

	// url.RawPath will be valid if path has any encoded characters, if not it will
	// be empty - in which case we need to consider url.Path (bug in net/http?)
	encodedResource := r.URL.RawPath
	encodedQuery := r.URL.RawQuery
	if encodedResource == "" {
		splits := strings.Split(r.URL.Path, "?")
		if len(splits) > 0 {
			encodedResource = splits[0]
		}
	}
	queries := strings.Split(encodedQuery, "&")
	var filteredQueries []string
	var gotSignature string
	var expires string
	var accessKey string
	var err error
	for _, query := range queries {
		keyval := strings.Split(query, "=")
		switch keyval[0] {
		case "AWSAccessKeyId":
			accessKey, err = url.QueryUnescape(keyval[1])
		case "Signature":
			gotSignature, err = url.QueryUnescape(keyval[1])
		case "Expires":
			expires, err = url.QueryUnescape(keyval[1])
		default:
			filteredQueries = append(filteredQueries, query)
		}
	}
	// Check if the query unescaped properly.
	if err != nil {
		errorIf(err, "Unable to unescape query values", queries)
		return ErrInvalidQueryParams
	}

	// Invalid access key.
	if accessKey == "" {
		return ErrInvalidQueryParams
	}

	// Validate if access key id same.
	if accessKey != cred.AccessKey {
		return ErrInvalidAccessKeyID
	}

	// Make sure the request has not expired.
	expiresInt, err := strconv.ParseInt(expires, 10, 64)
	if err != nil {
		return ErrMalformedExpires
	}

	// Check if the presigned URL has expired.
	if expiresInt < time.Now().UTC().Unix() {
		return ErrExpiredPresignRequest
	}

	expectedSignature := preSignatureV2(r.Method, encodedResource, strings.Join(filteredQueries, "&"), r.Header, expires)
	if gotSignature != expectedSignature {
		return ErrSignatureDoesNotMatch
	}

	return ErrNone
}

// Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
// Signature = Base64( HMAC-SHA1( YourSecretKey, UTF-8-Encoding-Of( StringToSign ) ) );
//
// StringToSign = HTTP-Verb + "\n" +
//  	Content-Md5 + "\n" +
//  	Content-Type + "\n" +
//  	Date + "\n" +
//  	CanonicalizedProtocolHeaders +
//  	CanonicalizedResource;
//
// CanonicalizedResource = [ "/" + Bucket ] +
//  	<HTTP-Request-URI, from the protocol name up to the query string> +
//  	[ subresource, if present. For example "?acl", "?location", "?logging", or "?torrent"];
//
// CanonicalizedProtocolHeaders = <described below>

// doesSignV2Match - Verify authorization header with calculated header in accordance with
//     - http://docs.aws.amazon.com/AmazonS3/latest/dev/auth-request-sig-v2.html
// returns true if matches, false otherwise. if error is not nil then it is always false

func validateV2AuthHeader(v2Auth string) APIErrorCode {
	if v2Auth == "" {
		return ErrAuthHeaderEmpty
	}
	// Verify if the header algorithm is supported or not.
	if !strings.HasPrefix(v2Auth, signV2Algorithm) {
		return ErrSignatureVersionNotSupported
	}

	// below is V2 Signed Auth header format, splitting on `space` (after the `AWS` string).
	// Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature
	authFields := strings.Split(v2Auth, " ")
	if len(authFields) != 2 {
		return ErrMissingFields
	}

	// Then will be splitting on ":", this will seprate `AWSAccessKeyId` and `Signature` string.
	keySignFields := strings.Split(strings.TrimSpace(authFields[1]), ":")
	if len(keySignFields) != 2 {
		return ErrMissingFields
	}

	// Access credentials.
	cred := serverConfig.GetCredential()
	if keySignFields[0] != cred.AccessKey {
		return ErrInvalidAccessKeyID
	}

	return ErrNone
}

func doesSignV2Match(r *http.Request) APIErrorCode {
	v2Auth := r.Header.Get("Authorization")

	if apiError := validateV2AuthHeader(v2Auth); apiError != ErrNone {
		return apiError
	}

	// Encode path:
	//   url.RawPath will be valid if path has any encoded characters, if not it will
	//   be empty - in which case we need to consider url.Path (bug in net/http?)
	encodedResource := r.URL.RawPath
	if encodedResource == "" {
		splits := strings.Split(r.URL.Path, "?")
		if len(splits) > 0 {
			encodedResource = getURLEncodedName(splits[0])
		}
	}

	// Encode query strings
	encodedQuery := r.URL.Query().Encode()

	expectedAuth := signatureV2(r.Method, encodedResource, encodedQuery, r.Header)
	if v2Auth != expectedAuth {
		return ErrSignatureDoesNotMatch
	}

	return ErrNone
}

func calculateSignatureV2(stringToSign string, secret string) string {
	hm := hmac.New(sha1.New, []byte(secret))
	hm.Write([]byte(stringToSign))
	return base64.StdEncoding.EncodeToString(hm.Sum(nil))
}

// Return signature-v2 for the presigned request.
func preSignatureV2(method string, encodedResource string, encodedQuery string, headers http.Header, expires string) string {
	cred := serverConfig.GetCredential()
	stringToSign := presignV2STS(method, encodedResource, encodedQuery, headers, expires)
	return calculateSignatureV2(stringToSign, cred.SecretKey)
}

// Return signature-v2 authrization header.
func signatureV2(method string, encodedResource string, encodedQuery string, headers http.Header) string {
	cred := serverConfig.GetCredential()
	stringToSign := signV2STS(method, encodedResource, encodedQuery, headers)
	signature := calculateSignatureV2(stringToSign, cred.SecretKey)
	return fmt.Sprintf("%s %s:%s", signV2Algorithm, cred.AccessKey, signature)
}

// Return canonical headers.
func canonicalizedAmzHeadersV2(headers http.Header) string {
	var keys []string
	keyval := make(map[string]string)
	for key := range headers {
		lkey := strings.ToLower(key)
		if !strings.HasPrefix(lkey, "x-amz-") {
			continue
		}
		keys = append(keys, lkey)
		keyval[lkey] = strings.Join(headers[key], ",")
	}
	sort.Strings(keys)
	var canonicalHeaders []string
	for _, key := range keys {
		canonicalHeaders = append(canonicalHeaders, key+":"+keyval[key])
	}
	return strings.Join(canonicalHeaders, "\n")
}

// Return canonical resource string.
func canonicalizedResourceV2(encodedPath string, encodedQuery string) string {
	queries := strings.Split(encodedQuery, "&")
	keyval := make(map[string]string)
	for _, query := range queries {
		key := query
		val := ""
		index := strings.Index(query, "=")
		if index != -1 {
			key = query[:index]
			val = query[index+1:]
		}
		keyval[key] = val
	}
	var canonicalQueries []string
	for _, key := range resourceList {
		val, ok := keyval[key]
		if !ok {
			continue
		}
		if val == "" {
			canonicalQueries = append(canonicalQueries, key)
			continue
		}
		canonicalQueries = append(canonicalQueries, key+"="+val)
	}
	if len(canonicalQueries) == 0 {
		return encodedPath
	}
	// the queries will be already sorted as resourceList is sorted.
	return encodedPath + "?" + strings.Join(canonicalQueries, "&")
}

// Return string to sign for authz header calculation.
func signV2STS(method string, encodedResource string, encodedQuery string, headers http.Header) string {
	canonicalHeaders := canonicalizedAmzHeadersV2(headers)
	if len(canonicalHeaders) > 0 {
		canonicalHeaders += "\n"
	}

	// From the Amazon docs:
	//
	// StringToSign = HTTP-Verb + "\n" +
	// 	 Content-Md5 + "\n" +
	//	 Content-Type + "\n" +
	//	 Date + "\n" +
	//	 CanonicalizedProtocolHeaders +
	//	 CanonicalizedResource;
	stringToSign := strings.Join([]string{
		method,
		headers.Get("Content-MD5"),
		headers.Get("Content-Type"),
		headers.Get("Date"),
		canonicalHeaders,
	}, "\n") + canonicalizedResourceV2(encodedResource, encodedQuery)

	return stringToSign
}

// Return string to sign for pre-sign signature calculation.
func presignV2STS(method string, encodedResource string, encodedQuery string, headers http.Header, expires string) string {
	canonicalHeaders := canonicalizedAmzHeadersV2(headers)
	if len(canonicalHeaders) > 0 {
		canonicalHeaders += "\n"
	}

	// From the Amazon docs:
	//
	// StringToSign = HTTP-Verb + "\n" +
	// 	 Content-Md5 + "\n" +
	//	 Content-Type + "\n" +
	//	 Expires + "\n" +
	//	 CanonicalizedProtocolHeaders +
	//	 CanonicalizedResource;
	stringToSign := strings.Join([]string{
		method,
		headers.Get("Content-MD5"),
		headers.Get("Content-Type"),
		expires,
		canonicalHeaders,
	}, "\n") + canonicalizedResourceV2(encodedResource, encodedQuery)
	return stringToSign
}
<commit_msg>Presign V2: Unescape non-std queries in urls (#3549)<commit_after>/*
 * Minio Cloud Storage, (C) 2016 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

// Signature and API related constants.
const (
	signV2Algorithm = "AWS"
)

// AWS S3 Signature V2 calculation rule is give here:
// http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationStringToSign

// Whitelist resource list that will be used in query string for signature-V2 calculation.
// The list should be alphabetically sorted
var resourceList = []string{
	"acl",
	"delete",
	"lifecycle",
	"location",
	"logging",
	"notification",
	"partNumber",
	"policy",
	"requestPayment",
	"response-cache-control",
	"response-content-disposition",
	"response-content-encoding",
	"response-content-language",
	"response-content-type",
	"response-expires",
	"torrent",
	"uploadId",
	"uploads",
	"versionId",
	"versioning",
	"versions",
	"website",
}

func doesPolicySignatureV2Match(formValues map[string]string) APIErrorCode {
	cred := serverConfig.GetCredential()
	accessKey := formValues["Awsaccesskeyid"]
	if accessKey != cred.AccessKey {
		return ErrInvalidAccessKeyID
	}
	signature := formValues["Signature"]
	policy := formValues["Policy"]
	if signature != calculateSignatureV2(policy, cred.SecretKey) {
		return ErrSignatureDoesNotMatch
	}
	return ErrNone
}

// doesPresignV2SignatureMatch - Verify query headers with presigned signature
//     - http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationQueryStringAuth
// returns ErrNone if matches. S3 errors otherwise.
func doesPresignV2SignatureMatch(r *http.Request) APIErrorCode {
	// Access credentials.
	cred := serverConfig.GetCredential()

	// url.RawPath will be valid if path has any encoded characters, if not it will
	// be empty - in which case we need to consider url.Path (bug in net/http?)
	encodedResource := r.URL.RawPath
	encodedQuery := r.URL.RawQuery
	if encodedResource == "" {
		splits := strings.Split(r.URL.Path, "?")
		if len(splits) > 0 {
			encodedResource = splits[0]
		}
	}
	queries := strings.Split(encodedQuery, "&")
	var filteredQueries []string
	var gotSignature string
	var expires string
	var accessKey string
	var err error
	for _, query := range queries {
		keyval := strings.Split(query, "=")
		switch keyval[0] {
		case "AWSAccessKeyId":
			accessKey, err = url.QueryUnescape(keyval[1])
		case "Signature":
			gotSignature, err = url.QueryUnescape(keyval[1])
		case "Expires":
			expires, err = url.QueryUnescape(keyval[1])
		default:
			unescapedQuery, qerr := url.QueryUnescape(query)
			if qerr == nil {
				filteredQueries = append(filteredQueries, unescapedQuery)
			} else {
				err = qerr
			}
		}
		// Check if the query unescaped properly.
		if err != nil {
			errorIf(err, "Unable to unescape query values", queries)
			return ErrInvalidQueryParams
		}
	}

	// Invalid access key.
	if accessKey == "" {
		return ErrInvalidQueryParams
	}

	// Validate if access key id same.
	if accessKey != cred.AccessKey {
		return ErrInvalidAccessKeyID
	}

	// Make sure the request has not expired.
	expiresInt, err := strconv.ParseInt(expires, 10, 64)
	if err != nil {
		return ErrMalformedExpires
	}

	// Check if the presigned URL has expired.
	if expiresInt < time.Now().UTC().Unix() {
		return ErrExpiredPresignRequest
	}

	expectedSignature := preSignatureV2(r.Method, encodedResource, strings.Join(filteredQueries, "&"), r.Header, expires)
	if gotSignature != expectedSignature {
		return ErrSignatureDoesNotMatch
	}

	return ErrNone
}

// Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
// Signature = Base64( HMAC-SHA1( YourSecretKey, UTF-8-Encoding-Of( StringToSign ) ) );
//
// StringToSign = HTTP-Verb + "\n" +
//  	Content-Md5 + "\n" +
//  	Content-Type + "\n" +
//  	Date + "\n" +
//  	CanonicalizedProtocolHeaders +
//  	CanonicalizedResource;
//
// CanonicalizedResource = [ "/" + Bucket ] +
//  	<HTTP-Request-URI, from the protocol name up to the query string> +
//  	[ subresource, if present. For example "?acl", "?location", "?logging", or "?torrent"];
//
// CanonicalizedProtocolHeaders = <described below>

// doesSignV2Match - Verify authorization header with calculated header in accordance with
//     - http://docs.aws.amazon.com/AmazonS3/latest/dev/auth-request-sig-v2.html
// returns true if matches, false otherwise. if error is not nil then it is always false

func validateV2AuthHeader(v2Auth string) APIErrorCode {
	if v2Auth == "" {
		return ErrAuthHeaderEmpty
	}
	// Verify if the header algorithm is supported or not.
	if !strings.HasPrefix(v2Auth, signV2Algorithm) {
		return ErrSignatureVersionNotSupported
	}

	// below is V2 Signed Auth header format, splitting on `space` (after the `AWS` string).
	// Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature
	authFields := strings.Split(v2Auth, " ")
	if len(authFields) != 2 {
		return ErrMissingFields
	}

	// Then will be splitting on ":", this will seprate `AWSAccessKeyId` and `Signature` string.
	keySignFields := strings.Split(strings.TrimSpace(authFields[1]), ":")
	if len(keySignFields) != 2 {
		return ErrMissingFields
	}

	// Access credentials.
	cred := serverConfig.GetCredential()
	if keySignFields[0] != cred.AccessKey {
		return ErrInvalidAccessKeyID
	}

	return ErrNone
}

func doesSignV2Match(r *http.Request) APIErrorCode {
	v2Auth := r.Header.Get("Authorization")

	if apiError := validateV2AuthHeader(v2Auth); apiError != ErrNone {
		return apiError
	}

	// Encode path:
	//   url.RawPath will be valid if path has any encoded characters, if not it will
	//   be empty - in which case we need to consider url.Path (bug in net/http?)
	encodedResource := r.URL.RawPath
	if encodedResource == "" {
		splits := strings.Split(r.URL.Path, "?")
		if len(splits) > 0 {
			encodedResource = getURLEncodedName(splits[0])
		}
	}

	// Encode query strings
	encodedQuery := r.URL.Query().Encode()

	expectedAuth := signatureV2(r.Method, encodedResource, encodedQuery, r.Header)
	if v2Auth != expectedAuth {
		return ErrSignatureDoesNotMatch
	}

	return ErrNone
}

func calculateSignatureV2(stringToSign string, secret string) string {
	hm := hmac.New(sha1.New, []byte(secret))
	hm.Write([]byte(stringToSign))
	return base64.StdEncoding.EncodeToString(hm.Sum(nil))
}

// Return signature-v2 for the presigned request.
func preSignatureV2(method string, encodedResource string, encodedQuery string, headers http.Header, expires string) string {
	cred := serverConfig.GetCredential()
	stringToSign := presignV2STS(method, encodedResource, encodedQuery, headers, expires)
	return calculateSignatureV2(stringToSign, cred.SecretKey)
}

// Return signature-v2 authrization header.
func signatureV2(method string, encodedResource string, encodedQuery string, headers http.Header) string {
	cred := serverConfig.GetCredential()
	stringToSign := signV2STS(method, encodedResource, encodedQuery, headers)
	signature := calculateSignatureV2(stringToSign, cred.SecretKey)
	return fmt.Sprintf("%s %s:%s", signV2Algorithm, cred.AccessKey, signature)
}

// Return canonical headers.
func canonicalizedAmzHeadersV2(headers http.Header) string {
	var keys []string
	keyval := make(map[string]string)
	for key := range headers {
		lkey := strings.ToLower(key)
		if !strings.HasPrefix(lkey, "x-amz-") {
			continue
		}
		keys = append(keys, lkey)
		keyval[lkey] = strings.Join(headers[key], ",")
	}
	sort.Strings(keys)
	var canonicalHeaders []string
	for _, key := range keys {
		canonicalHeaders = append(canonicalHeaders, key+":"+keyval[key])
	}
	return strings.Join(canonicalHeaders, "\n")
}

// Return canonical resource string.
func canonicalizedResourceV2(encodedPath string, encodedQuery string) string {
	queries := strings.Split(encodedQuery, "&")
	keyval := make(map[string]string)
	for _, query := range queries {
		key := query
		val := ""
		index := strings.Index(query, "=")
		if index != -1 {
			key = query[:index]
			val = query[index+1:]
		}
		keyval[key] = val
	}
	var canonicalQueries []string
	for _, key := range resourceList {
		val, ok := keyval[key]
		if !ok {
			continue
		}
		if val == "" {
			canonicalQueries = append(canonicalQueries, key)
			continue
		}
		canonicalQueries = append(canonicalQueries, key+"="+val)
	}
	if len(canonicalQueries) == 0 {
		return encodedPath
	}
	// the queries will be already sorted as resourceList is sorted.
	return encodedPath + "?" + strings.Join(canonicalQueries, "&")
}

// Return string to sign for authz header calculation.
func signV2STS(method string, encodedResource string, encodedQuery string, headers http.Header) string {
	canonicalHeaders := canonicalizedAmzHeadersV2(headers)
	if len(canonicalHeaders) > 0 {
		canonicalHeaders += "\n"
	}

	// From the Amazon docs:
	//
	// StringToSign = HTTP-Verb + "\n" +
	// 	 Content-Md5 + "\n" +
	//	 Content-Type + "\n" +
	//	 Date + "\n" +
	//	 CanonicalizedProtocolHeaders +
	//	 CanonicalizedResource;
	stringToSign := strings.Join([]string{
		method,
		headers.Get("Content-MD5"),
		headers.Get("Content-Type"),
		headers.Get("Date"),
		canonicalHeaders,
	}, "\n") + canonicalizedResourceV2(encodedResource, encodedQuery)

	return stringToSign
}

// Return string to sign for pre-sign signature calculation.
func presignV2STS(method string, encodedResource string, encodedQuery string, headers http.Header, expires string) string {
	canonicalHeaders := canonicalizedAmzHeadersV2(headers)
	if len(canonicalHeaders) > 0 {
		canonicalHeaders += "\n"
	}

	// From the Amazon docs:
	//
	// StringToSign = HTTP-Verb + "\n" +
	// 	 Content-Md5 + "\n" +
	//	 Content-Type + "\n" +
	//	 Expires + "\n" +
	//	 CanonicalizedProtocolHeaders +
	//	 CanonicalizedResource;
	stringToSign := strings.Join([]string{
		method,
		headers.Get("Content-MD5"),
		headers.Get("Content-Type"),
		expires,
		canonicalHeaders,
	}, "\n") + canonicalizedResourceV2(encodedResource, encodedQuery)
	return stringToSign
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"

	"github.com/davecgh/go-spew/spew"

	"github.com/donatj/sqlread"
	"github.com/donatj/sqlread/mapcache"
)

var filename string

var (
	nocache = flag.Bool("nocache", false, "disable caching")
)

func init() {
	flag.Parse()

	filename = flag.Arg(0)
}

func main() {
	// return
	log.Println("starting initial pass")

	unbuff, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}

	cache := mapcache.New(unbuff)
	tree, err := cache.Get()
	if err != nil && err != mapcache.ErrCacheMiss {
		log.Fatal(err)
	}

	fmt.Println(*nocache)

	if err == mapcache.ErrCacheMiss || *nocache {
		l, li := sqlread.Lex(unbuff)
		go func() {
			l.Run(sqlread.StartState)
		}()

		sp := sqlread.NewSummaryParser()

		p := sqlread.Parse(li)
		err = p.Run(sp.ParseStart)
		if err != nil {
			log.Fatal(err)
		}

		if !*nocache {
			cache.Store(sp.Tree)
		}

		tree = sp.Tree
	} else {
		log.Println("loaded from cache")
	}

	log.Println("finished initial pass")

	//for tbl, _ := range t {
	//	fmt.Println(tbl)
	//}

	_ = tree

	interactive()
}

func interactive() {
	sw := NewStdinWrap(os.Stdin)

	for {
		stdinlex, stdli := sqlread.Lex(sw)
		go func() {
			stdinlex.Run(sqlread.StartIntpState)
		}()

		qp := sqlread.NewQueryParser()

		p := sqlread.Parse(stdli)
		err := p.Run(qp.ParseStart)
		if err != nil {
			log.Println(err)
			continue
		}

		spew.Dump(qp.Tree)

		// for {
		// 	x, ok := <-stdli
		// 	if !ok {
		// 		break
		// 	}

		// 	spew.Dump(x)

		// }

		sw.Flush()
		log.Println("restarting lexer")
	}
}
<commit_msg>Its ALIVE!<commit_after>package main

import (
	"encoding/csv"
	"flag"
	"fmt"
	"io"
	"log"
	"os"

	"github.com/davecgh/go-spew/spew"

	"github.com/donatj/sqlread"
	"github.com/donatj/sqlread/mapcache"
)

var filename string

var (
	nocache = flag.Bool("nocache", false, "disable caching")
)

func init() {
	flag.Parse()

	filename = flag.Arg(0)
}

func main() {
	// return
	log.Println("starting initial pass")

	unbuff, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}

	cache := mapcache.New(unbuff)
	tree, err := cache.Get()
	if err != nil && err != mapcache.ErrCacheMiss {
		log.Fatal(err)
	}

	fmt.Println(*nocache)

	if err == mapcache.ErrCacheMiss || *nocache {
		l, li := sqlread.Lex(unbuff)
		go func() {
			l.Run(sqlread.StartState)
		}()

		sp := sqlread.NewSummaryParser()

		p := sqlread.Parse(li)
		err = p.Run(sp.ParseStart)
		if err != nil {
			log.Fatal(err)
		}

		if !*nocache {
			cache.Store(sp.Tree)
		}

		tree = sp.Tree
	} else {
		log.Println("loaded from cache")
	}

	log.Println("finished initial pass")

	//for tbl, _ := range t {
	//	fmt.Println(tbl)
	//}

	_ = tree

	interactive(tree, unbuff)
}

func interactive(tree sqlread.SummaryTree, buff io.ReaderAt) {
	w := csv.NewWriter(os.Stdout)
	sw := NewStdinWrap(os.Stdin)

	for {
		stdinlex, stdli := sqlread.Lex(sw)
		go func() {
			stdinlex.Run(sqlread.StartIntpState)
		}()

		qp := sqlread.NewQueryParser()

		p := sqlread.Parse(stdli)
		err := p.Run(qp.ParseStart)
		if err != nil {
			log.Println("query error: ", err)
			sw.Flush()
			continue
		}

		spew.Dump(qp.Tree)

	queryloop:
		for _, qry := range qp.Tree.Queries {
			tbl, tok := tree[qry.Table]
			if !tok {
				log.Printf("table `%s` not found", qry.Table)
				continue
			}

			colind := []int{}

			for _, col := range qry.Columns {
				found := false
				for tci, tcol := range tbl.Cols {
					if col == "*" || col == tcol.Name {
						found = true
						colind = append(colind, tci)
					}
				}

				if !found {
					log.Printf("error: column `%s` not found", col)
					continue queryloop
				}
			}

			spew.Dump(colind)

			for _, loc := range tbl.DataLocs {
				start := loc.Start.Pos
				end := loc.End.Pos

				sl, sli := sqlread.LexSection(buff, start, end-start+1)
				go func() {
					sl.Run(sqlread.StartState)
				}()

				sp := sqlread.NewInsertDetailParser()

				spr := sqlread.Parse(sli)
				go func() {
					err := spr.Run(sp.ParseStart)
					if err != nil {
						log.Fatal(err)
					}
				}()

				for {
					row, ok := <-sp.Out
					if !ok {
						w.Flush()
						break
					}

					out := make([]string, len(colind))
					for i, ci := range colind {
						out[i] = row[ci]
					}

					w.Write(out)
				}
			}
		}

		for i := uint(0); i < qp.Tree.ShowTables; i++ {
			for cv, _ := range tree {
				w.Write([]string{cv})
			}

			w.Flush()
		}

		if qp.Tree.Quit {
			return
		}

		// for {
		// 	x, ok := <-stdli
		// 	if !ok {
		// 		break
		// 	}

		// 	spew.Dump(x)

		// }

		sw.Flush()
		log.Println("restarting lexer")
	}
}
<|endoftext|>


<commit_before>// Downloads torrents from the command-line.
package main

import (
	"expvar"
	"fmt"
	stdLog "log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/anacrolix/missinggo"
	"github.com/dustin/go-humanize"
	"golang.org/x/xerrors"

	"github.com/anacrolix/log"

	"github.com/anacrolix/envpprof"
	"github.com/anacrolix/tagflag"
	"golang.org/x/time/rate"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/iplist"
	"github.com/anacrolix/torrent/metainfo"
	"github.com/anacrolix/torrent/storage"
)

func torrentBar(t *torrent.Torrent, pieceStates bool) {
	go func() {
		if t.Info() == nil {
			fmt.Printf("getting info for %q\n", t.Name())
			<-t.GotInfo()
		}
		var lastLine string
		for {
			var completedPieces, partialPieces int
			psrs := t.PieceStateRuns()
			for _, r := range psrs {
				if r.Complete {
					completedPieces += r.Length
				}
				if r.Partial {
					partialPieces += r.Length
				}
			}
			line := fmt.Sprintf(
				"downloading %q: %s/%s, %d/%d pieces completed (%d partial)\n",
				t.Name(),
				humanize.Bytes(uint64(t.BytesCompleted())),
				humanize.Bytes(uint64(t.Length())),
				completedPieces,
				t.NumPieces(),
				partialPieces,
			)
			if line != lastLine {
				lastLine = line
				os.Stdout.WriteString(line)
			}
			if pieceStates {
				fmt.Println(psrs)
			}
			time.Sleep(time.Second)
		}
	}()
}

type stringAddr string

func (stringAddr) Network() string   { return "" }
func (me stringAddr) String() string { return string(me) }

func resolveTestPeers(addrs []string) (ret []torrent.PeerInfo) {
	for _, ta := range flags.TestPeer {
		ret = append(ret, torrent.PeerInfo{
			Addr: stringAddr(ta),
		})
	}
	return
}

func addTorrents(client *torrent.Client) error {
	testPeers := resolveTestPeers(flags.TestPeer)
	for _, arg := range flags.Torrent {
		t, err := func() (*torrent.Torrent, error) {
			if strings.HasPrefix(arg, "magnet:") {
				t, err := client.AddMagnet(arg)
				if err != nil {
					return nil, xerrors.Errorf("error adding magnet: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
				response, err := http.Get(arg)
				if err != nil {
					return nil, xerrors.Errorf("Error downloading torrent file: %s", err)
				}

				metaInfo, err := metainfo.Load(response.Body)
				defer response.Body.Close()
				if err != nil {
					return nil, xerrors.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, xerrors.Errorf("adding torrent: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "infohash:") {
				t, _ := client.AddTorrentInfoHash(metainfo.NewHashFromHex(strings.TrimPrefix(arg, "infohash:")))
				return t, nil
			} else {
				metaInfo, err := metainfo.LoadFromFile(arg)
				if err != nil {
					return nil, xerrors.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, xerrors.Errorf("adding torrent: %w", err)
				}
				return t, nil
			}
		}()
		if err != nil {
			return xerrors.Errorf("adding torrent for %q: %w", arg, err)
		}
		if flags.Progress {
			torrentBar(t, flags.PieceStates)
		}
		t.AddPeers(testPeers)
		go func() {
			<-t.GotInfo()
			t.DownloadAll()
		}()
	}
	return nil
}

var flags = struct {
	Mmap            bool          `help:"memory-map torrent data"`
	TestPeer        []string      `help:"addresses of some starting peers"`
	Seed            bool          `help:"seed after download is complete"`
	Addr            string        `help:"network listen addr"`
	UploadRate      tagflag.Bytes `help:"max piece bytes to send per second"`
	DownloadRate    tagflag.Bytes `help:"max bytes per second down from peers"`
	Debug           bool
	PackedBlocklist string
	Stats           *bool
	PublicIP        net.IP
	Progress        bool
	PieceStates     bool
	Quiet           bool `help:"discard client logging"`
	Dht             bool

	TcpPeers        bool
	UtpPeers        bool
	Webtorrent      bool
	DisableWebseeds bool

	Ipv4 bool
	Ipv6 bool
	Pex  bool

	tagflag.StartPos

	Torrent []string `arity:"+" help:"torrent file path or magnet uri"`
}{
	UploadRate:   -1,
	DownloadRate: -1,
	Progress:     true,
	Dht:          true,

	TcpPeers:   true,
	UtpPeers:   true,
	Webtorrent: true,

	Ipv4: true,
	Ipv6: true,
	Pex:  true,
}

func stdoutAndStderrAreSameFile() bool {
	fi1, _ := os.Stdout.Stat()
	fi2, _ := os.Stderr.Stat()
	return os.SameFile(fi1, fi2)
}

func statsEnabled() bool {
	if flags.Stats == nil {
		return flags.Debug
	}
	return *flags.Stats
}

func exitSignalHandlers(notify *missinggo.SynchronizedEvent) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	for {
		log.Printf("close signal received: %+v", <-c)
		notify.Set()
	}
}

func main() {
	if err := mainErr(); err != nil {
		log.Printf("error in main: %v", err)
		os.Exit(1)
	}
}

func mainErr() error {
	stdLog.SetFlags(stdLog.Flags() | stdLog.Lshortfile)
	var flags struct {
		tagflag.StartPos
		Command string
		Args    tagflag.ExcessArgs
	}
	parser := tagflag.Parse(&flags, tagflag.ParseIntermixed(false))
	switch flags.Command {
	case "announce":
		return announceErr(flags.Args, parser)
	case "download":
		return downloadErr(flags.Args, parser)
	default:
		return fmt.Errorf("unknown command %q", flags.Command)
	}
}

func downloadErr(args []string, parent *tagflag.Parser) error {
	tagflag.ParseArgs(&flags, args, tagflag.Parent(parent))
	defer envpprof.Stop()
	clientConfig := torrent.NewDefaultClientConfig()
	clientConfig.DisableWebseeds = flags.DisableWebseeds
	clientConfig.DisableTCP = !flags.TcpPeers
	clientConfig.DisableUTP = !flags.UtpPeers
	clientConfig.DisableIPv4 = !flags.Ipv4
	clientConfig.DisableIPv6 = !flags.Ipv6
	clientConfig.DisableAcceptRateLimiting = true
	clientConfig.NoDHT = !flags.Dht
	clientConfig.Debug = flags.Debug
	clientConfig.Seed = flags.Seed
	clientConfig.PublicIp4 = flags.PublicIP
	clientConfig.PublicIp6 = flags.PublicIP
	clientConfig.DisablePEX = !flags.Pex
	clientConfig.DisableWebtorrent = !flags.Webtorrent
	if flags.PackedBlocklist != "" {
		blocklist, err := iplist.MMapPackedFile(flags.PackedBlocklist)
		if err != nil {
			return xerrors.Errorf("loading blocklist: %v", err)
		}
		defer blocklist.Close()
		clientConfig.IPBlocklist = blocklist
	}
	if flags.Mmap {
		clientConfig.DefaultStorage = storage.NewMMap("")
	}
	if flags.Addr != "" {
		clientConfig.SetListenAddr(flags.Addr)
	}
	if flags.UploadRate != -1 {
		clientConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(flags.UploadRate), 256<<10)
	}
	if flags.DownloadRate != -1 {
		clientConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(flags.DownloadRate), 1<<20)
	}
	if flags.Quiet {
		clientConfig.Logger = log.Discard
	}

	var stop missinggo.SynchronizedEvent
	defer func() {
		stop.Set()
	}()

	client, err := torrent.NewClient(clientConfig)
	if err != nil {
		return xerrors.Errorf("creating client: %v", err)
	}
	defer client.Close()
	go exitSignalHandlers(&stop)
	go func() {
		<-stop.C()
		client.Close()
	}()

	// Write status on the root path on the default HTTP muxer. This will be bound to localhost
	// somewhere if GOPPROF is set, thanks to the envpprof import.
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		client.WriteStatus(w)
	})
	err = addTorrents(client)
	if err != nil {
		return fmt.Errorf("adding torrents: %w", err)
	}
	defer outputStats(client)
	if client.WaitAll() {
		log.Print("downloaded ALL the torrents")
	} else {
		return xerrors.New("y u no complete torrents?!")
	}
	if flags.Seed {
		outputStats(client)
		<-stop.C()
	}
	return nil
}

func outputStats(cl *torrent.Client) {
	if !statsEnabled() {
		return
	}
	expvar.Do(func(kv expvar.KeyValue) {
		fmt.Printf("%s: %s\n", kv.Key, kv.Value)
	})
	cl.WriteStatus(os.Stdout)
}
<commit_msg>cmd/torrent: Use alexflint/go-arg for argument parsing<commit_after>// Downloads torrents from the command-line.
package main

import (
	"expvar"
	"fmt"
	stdLog "log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/alexflint/go-arg"
	"github.com/anacrolix/missinggo"
	"github.com/dustin/go-humanize"
	"golang.org/x/xerrors"

	"github.com/anacrolix/log"

	"github.com/anacrolix/envpprof"
	"github.com/anacrolix/tagflag"
	"golang.org/x/time/rate"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/iplist"
	"github.com/anacrolix/torrent/metainfo"
	"github.com/anacrolix/torrent/storage"
)

func torrentBar(t *torrent.Torrent, pieceStates bool) {
	go func() {
		if t.Info() == nil {
			fmt.Printf("getting info for %q\n", t.Name())
			<-t.GotInfo()
		}
		var lastLine string
		for {
			var completedPieces, partialPieces int
			psrs := t.PieceStateRuns()
			for _, r := range psrs {
				if r.Complete {
					completedPieces += r.Length
				}
				if r.Partial {
					partialPieces += r.Length
				}
			}
			line := fmt.Sprintf(
				"downloading %q: %s/%s, %d/%d pieces completed (%d partial)\n",
				t.Name(),
				humanize.Bytes(uint64(t.BytesCompleted())),
				humanize.Bytes(uint64(t.Length())),
				completedPieces,
				t.NumPieces(),
				partialPieces,
			)
			if line != lastLine {
				lastLine = line
				os.Stdout.WriteString(line)
			}
			if pieceStates {
				fmt.Println(psrs)
			}
			time.Sleep(time.Second)
		}
	}()
}

type stringAddr string

func (stringAddr) Network() string   { return "" }
func (me stringAddr) String() string { return string(me) }

func resolveTestPeers(addrs []string) (ret []torrent.PeerInfo) {
	for _, ta := range flags.TestPeer {
		ret = append(ret, torrent.PeerInfo{
			Addr: stringAddr(ta),
		})
	}
	return
}

func addTorrents(client *torrent.Client) error {
	testPeers := resolveTestPeers(flags.TestPeer)
	for _, arg := range flags.Torrent {
		t, err := func() (*torrent.Torrent, error) {
			if strings.HasPrefix(arg, "magnet:") {
				t, err := client.AddMagnet(arg)
				if err != nil {
					return nil, xerrors.Errorf("error adding magnet: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
				response, err := http.Get(arg)
				if err != nil {
					return nil, xerrors.Errorf("Error downloading torrent file: %s", err)
				}

				metaInfo, err := metainfo.Load(response.Body)
				defer response.Body.Close()
				if err != nil {
					return nil, xerrors.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, xerrors.Errorf("adding torrent: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "infohash:") {
				t, _ := client.AddTorrentInfoHash(metainfo.NewHashFromHex(strings.TrimPrefix(arg, "infohash:")))
				return t, nil
			} else {
				metaInfo, err := metainfo.LoadFromFile(arg)
				if err != nil {
					return nil, xerrors.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, xerrors.Errorf("adding torrent: %w", err)
				}
				return t, nil
			}
		}()
		if err != nil {
			return xerrors.Errorf("adding torrent for %q: %w", arg, err)
		}
		if flags.Progress {
			torrentBar(t, flags.PieceStates)
		}
		t.AddPeers(testPeers)
		go func() {
			<-t.GotInfo()
			t.DownloadAll()
		}()
	}
	return nil
}

var flags struct {
	Debug bool
	Stats *bool

	*DownloadCmd  `arg:"subcommand:download"`
	*ListFilesCmd `arg:"subcommand:list-files"`
}

//DownloadCmd: &DownloadCmd{
//	UploadRate:   -1,
//	DownloadRate: -1,
//	Progress:     true,
//	Dht:          true,
//
//	TcpPeers:   true,
//	UtpPeers:   true,
//	Webtorrent: true,
//
//	Ipv4: true,
//	Ipv6: true,
//	Pex:  true,
//},

type DownloadCmd struct {
	Mmap            bool          `help:"memory-map torrent data"`
	TestPeer        []string      `help:"addresses of some starting peers"`
	Seed            bool          `help:"seed after download is complete"`
	Addr            string        `help:"network listen addr"`
	UploadRate      tagflag.Bytes `help:"max piece bytes to send per second" default:"-1"`
	DownloadRate    tagflag.Bytes `help:"max bytes per second down from peers"`
	PackedBlocklist string
	PublicIP        net.IP
	Progress        bool
	PieceStates     bool
	Quiet           bool `help:"discard client logging"`
	Dht             bool

	TcpPeers        bool
	UtpPeers        bool
	Webtorrent      bool
	DisableWebseeds bool

	Ipv4 bool
	Ipv6 bool
	Pex  bool

	Torrent []string `arity:"+" help:"torrent file path or magnet uri" arg:"positional"`
}

type ListFilesCmd struct {
	TorrentPath string `arg:"positional"`
}

func stdoutAndStderrAreSameFile() bool {
	fi1, _ := os.Stdout.Stat()
	fi2, _ := os.Stderr.Stat()
	return os.SameFile(fi1, fi2)
}

func statsEnabled() bool {
	if flags.Stats == nil {
		return flags.Debug
	}
	return *flags.Stats
}

func exitSignalHandlers(notify *missinggo.SynchronizedEvent) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	for {
		log.Printf("close signal received: %+v", <-c)
		notify.Set()
	}
}

func main() {
	if err := mainErr(); err != nil {
		log.Printf("error in main: %v", err)
		os.Exit(1)
	}
}

func mainErr() error {
	stdLog.SetFlags(stdLog.Flags() | stdLog.Lshortfile)
	p := arg.MustParse(&flags)
	switch {
	//case :
	//	return announceErr(flags.Args, parser)
	case flags.DownloadCmd != nil:
		return downloadErr()
	case flags.ListFilesCmd != nil:
		mi, err := metainfo.LoadFromFile(flags.ListFilesCmd.TorrentPath)
		if err != nil {
			return fmt.Errorf("loading from file %q: %v", flags.ListFilesCmd.TorrentPath, err)
		}
		info, err := mi.UnmarshalInfo()
		if err != nil {
			return fmt.Errorf("unmarshalling info from metainfo at %q: %v", flags.ListFilesCmd.TorrentPath, err)
		}
		for _, f := range info.UpvertedFiles() {
			fmt.Println(f.DisplayPath(&info))
		}
		return nil
	default:
		p.Fail(fmt.Sprintf("unexpected subcommand: %v", p.Subcommand()))
		panic("unreachable")
	}
}

func downloadErr() error {
	defer envpprof.Stop()
	clientConfig := torrent.NewDefaultClientConfig()
	clientConfig.DisableWebseeds = flags.DisableWebseeds
	clientConfig.DisableTCP = !flags.TcpPeers
	clientConfig.DisableUTP = !flags.UtpPeers
	clientConfig.DisableIPv4 = !flags.Ipv4
	clientConfig.DisableIPv6 = !flags.Ipv6
	clientConfig.DisableAcceptRateLimiting = true
	clientConfig.NoDHT = !flags.Dht
	clientConfig.Debug = flags.Debug
	clientConfig.Seed = flags.Seed
	clientConfig.PublicIp4 = flags.PublicIP
	clientConfig.PublicIp6 = flags.PublicIP
	clientConfig.DisablePEX = !flags.Pex
	clientConfig.DisableWebtorrent = !flags.Webtorrent
	if flags.PackedBlocklist != "" {
		blocklist, err := iplist.MMapPackedFile(flags.PackedBlocklist)
		if err != nil {
			return xerrors.Errorf("loading blocklist: %v", err)
		}
		defer blocklist.Close()
		clientConfig.IPBlocklist = blocklist
	}
	if flags.Mmap {
		clientConfig.DefaultStorage = storage.NewMMap("")
	}
	if flags.Addr != "" {
		clientConfig.SetListenAddr(flags.Addr)
	}
	if flags.UploadRate != -1 {
		clientConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(flags.UploadRate), 256<<10)
	}
	if flags.DownloadRate != -1 {
		clientConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(flags.DownloadRate), 1<<20)
	}
	if flags.Quiet {
		clientConfig.Logger = log.Discard
	}

	var stop missinggo.SynchronizedEvent
	defer func() {
		stop.Set()
	}()

	client, err := torrent.NewClient(clientConfig)
	if err != nil {
		return xerrors.Errorf("creating client: %v", err)
	}
	defer client.Close()
	go exitSignalHandlers(&stop)
	go func() {
		<-stop.C()
		client.Close()
	}()

	// Write status on the root path on the default HTTP muxer. This will be bound to localhost
	// somewhere if GOPPROF is set, thanks to the envpprof import.
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		client.WriteStatus(w)
	})
	err = addTorrents(client)
	if err != nil {
		return fmt.Errorf("adding torrents: %w", err)
	}
	defer outputStats(client)
	if client.WaitAll() {
		log.Print("downloaded ALL the torrents")
	} else {
		return xerrors.New("y u no complete torrents?!")
	}
	if flags.Seed {
		outputStats(client)
		<-stop.C()
	}
	return nil
}

func outputStats(cl *torrent.Client) {
	if !statsEnabled() {
		return
	}
	expvar.Do(func(kv expvar.KeyValue) {
		fmt.Printf("%s: %s\n", kv.Key, kv.Value)
	})
	cl.WriteStatus(os.Stdout)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/sahib/brig/cmd/tabwriter"

	"github.com/fatih/color"
	"github.com/sahib/brig/client"
	"github.com/urfave/cli"
)

func handleReset(ctx *cli.Context, ctl *client.Client) error {
	force := ctx.Bool("force")
	path := ctx.Args().First()
	rev := "HEAD"

	if len(ctx.Args()) > 1 {
		rev = ctx.Args().Get(1)
	}

	if err := ctl.Reset(path, rev, force); err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("unpin: %v", err)}
	}

	return nil
}

func commitName(cmt *client.Commit) string {
	if len(cmt.Tags) > 0 {
		return strings.ToUpper(cmt.Tags[0])
	}

	return cmt.Hash.ShortB58()
}

func handleHistory(ctx *cli.Context, ctl *client.Client) error {
	path := ctx.Args().First()

	history, err := ctl.History(path)
	if err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("history: %v", err)}
	}

	if _, err := ctl.Stat(path); err != nil {
		fmt.Printf("%s %s",
			color.YellowString("WARNING:"),
			`This file is not part of this commit, but there's still history for it.
         Most likely this file was moved or removed in the past.

`)
	}

	tabW := tabwriter.NewWriter(
		os.Stdout, 0, 0, 2, ' ',
		tabwriter.StripEscape,
	)

	if len(history) != 0 {
		fmt.Fprintf(tabW, "CHANGE\tFROM\tTO\tHOW\tWHEN\t\n")
	}

	for idx, entry := range history {
		what := ""
		printLine := true

		for _, detail := range entry.Mask {
			// If it was moved, let's display what moved.
			if detail == "moved" && idx+1 < len(history) {
				src := history[idx+1].Path
				dst := entry.Path

				if entry.ReferTo != "" {
					dst = entry.ReferTo
				}

				what = fmt.Sprintf(
					"%s → %s", color.RedString(src), color.RedString(dst),
				)
			}

			// Only display empty changes if nothing happened.
			if detail == "none" && !ctx.Bool("empty") {
				printLine = false
			}
		}
		if !printLine {
			continue
		}

		changeDesc := color.YellowString(strings.Join(entry.Mask, ", "))
		when := color.MagentaString(entry.Head.Date.Format(time.Stamp))

		fmt.Fprintf(
			tabW,
			"%s\t%s\t%s\t%s\t%s\t\n",
			changeDesc,
			color.CyanString(commitName(entry.Next)),
			color.GreenString(commitName(entry.Head)),
			what,
			when,
		)
	}

	return tabW.Flush()
}

func printDiffTree(diff *client.Diff) {
	const (
		diffTypeNone = iota
		diffTypeAdded
		diffTypeRemoved
		diffTypeIgnored
		diffTypeConflict
		diffTypeMerged
	)

	type diffEntry struct {
		typ  int
		pair client.DiffPair
	}

	entries := []client.StatInfo{}
	types := make(map[string]diffEntry)

	// Singular types:
	for _, info := range diff.Added {
		types[info.Path] = diffEntry{typ: diffTypeAdded}
		entries = append(entries, info)
	}
	for _, info := range diff.Removed {
		types[info.Path] = diffEntry{typ: diffTypeRemoved}
		entries = append(entries, info)
	}
	for _, info := range diff.Ignored {
		types[info.Path] = diffEntry{typ: diffTypeIgnored}
		entries = append(entries, info)
	}

	// Pair types:
	for _, pair := range diff.Conflict {
		types[pair.Dst.Path] = diffEntry{
			typ:  diffTypeConflict,
			pair: pair,
		}
		entries = append(entries, pair.Dst)
	}
	for _, pair := range diff.Merged {
		types[pair.Dst.Path] = diffEntry{
			typ:  diffTypeMerged,
			pair: pair,
		}
		entries = append(entries, pair.Dst)
	}

	if len(entries) == 0 {
		// Nothing to show:
		return
	}

	// Called to format each name in the resulting tree:
	formatter := func(n *treeNode) string {
		if n.name == "/" {
			return color.MagentaString("•")
		}

		if diffEntry, ok := types[n.entry.Path]; ok {
			switch diffEntry.typ {
			case diffTypeAdded:
				return color.GreenString(" + " + n.name)
			case diffTypeRemoved:
				return color.RedString(" - " + n.name)
			case diffTypeIgnored:
				return color.YellowString(" * " + n.name)
			case diffTypeMerged:
				name := fmt.Sprintf(
					" %s ⇄ %s",
					diffEntry.pair.Src.Path,
					diffEntry.pair.Dst.Path,
				)
				return color.CyanString(name)
			case diffTypeConflict:
				name := fmt.Sprintf(
					" %s ⚡ %s",
					diffEntry.pair.Src.Path,
					diffEntry.pair.Dst.Path,
				)
				return color.MagentaString(name)
			}
		}

		return n.name
	}

	// Render the tree:
	showTree(entries, &treeCfg{
		format:  formatter,
		showPin: false,
	})
}

func printDiff(diff *client.Diff) {
	simpleSection := func(heading string, infos []client.StatInfo) {
		if len(infos) == 0 {
			return
		}

		fmt.Println(heading)
		for _, info := range infos {
			fmt.Printf("  %s\n", info.Path)
		}

		fmt.Println()
	}

	pairSection := func(heading string, infos []client.DiffPair) {
		if len(infos) == 0 {
			return
		}

		for _, pair := range diff.Merged {
			fmt.Printf("  %s <-> %s\n", pair.Src.Path, pair.Dst.Path)
		}

		fmt.Println()
	}

	simpleSection(color.GreenString("Added:"), diff.Added)
	simpleSection(color.YellowString("Ignored:"), diff.Ignored)
	simpleSection(color.RedString("Removed:"), diff.Removed)

	pairSection(color.CyanString("Resolveable Conflicts:"), diff.Merged)
	pairSection(color.MagentaString("Conflicts:"), diff.Conflict)
}

func handleDiff(ctx *cli.Context, ctl *client.Client) error {
	if ctx.NArg() > 4 {
		fmt.Println("More than four arguments can't be handled.")
	}

	self, err := ctl.Whoami()
	if err != nil {
		return err
	}

	remoteName := self.CurrentUser
	localName := self.CurrentUser

	remoteRev := "HEAD"
	localRev := "HEAD"

	switch n := ctx.NArg(); {
	case n >= 1:
		remoteName = ctx.Args().Get(0)
		fallthrough
	case n >= 2:
		localName = ctx.Args().Get(1)
		fallthrough
	case n >= 3:
		remoteRev = ctx.Args().Get(2)
		fallthrough
	case n >= 4:
		localRev = ctx.Args().Get(3)
	}

	diff, err := ctl.MakeDiff(localName, remoteName, localRev, remoteRev)
	if err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("diff: %v", err)}
	}

	if ctx.Bool("list") {
		printDiff(diff)
	} else {
		printDiffTree(diff)
	}

	return nil
}

func handleFetch(ctx *cli.Context, ctl *client.Client) error {
	who := ctx.Args().First()
	return ctl.Fetch(who)
}

func handleSync(ctx *cli.Context, ctl *client.Client) error {
	who := ctx.Args().First()

	needFetch := true
	if ctx.Bool("no-fetch") {
		needFetch = false
	}

	return ctl.Sync(who, needFetch)
}

func handleStatus(ctx *cli.Context, ctl *client.Client) error {
	self, err := ctl.Whoami()
	if err != nil {
		return err
	}

	curr := self.CurrentUser
	diff, err := ctl.MakeDiff(curr, curr, "HEAD", "CURR")
	if err != nil {
		return err
	}

	if ctx.Bool("tree") {
		printDiffTree(diff)
	} else {
		printDiff(diff)
	}

	return nil
}

func handleBecome(ctx *cli.Context, ctl *client.Client) error {
	becomeSelf := ctx.Bool("self")
	if !becomeSelf && ctx.NArg() < 1 {
		return fmt.Errorf("become needs at least one argument without -s")
	}

	whoami, err := ctl.Whoami()
	if err != nil {
		return err
	}

	who := ctx.Args().First()
	if becomeSelf {
		who = whoami.Owner
	}

	if whoami.CurrentUser == who {
		fmt.Printf("You are already %s.\n", color.GreenString(who))
		return nil
	}

	if err := ctl.Become(who); err != nil {
		return err
	}

	suffix := "Changes will be local only."
	if who == whoami.Owner {
		suffix = "Welcome back!"
	}

	fmt.Printf(
		"You are viewing %s's data now. %s\n",
		color.GreenString(who), suffix,
	)
	return nil
}

func handleCommit(ctx *cli.Context, ctl *client.Client) error {
	msg := ""
	if msg = ctx.String("message"); msg == "" {
		msg = fmt.Sprintf("manual commit")
	}

	if err := ctl.MakeCommit(msg); err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("commit: %v", err)}
	}

	return nil
}

func handleTag(ctx *cli.Context, ctl *client.Client) error {
	if ctx.Bool("delete") {
		name := ctx.Args().Get(0)

		if err := ctl.Untag(name); err != nil {
			return ExitCode{
				UnknownError,
				fmt.Sprintf("untag: %v", err),
			}
		}
	} else {
		if len(ctx.Args()) < 2 {
			return ExitCode{BadArgs, "tag needs at least two arguments"}
		}

		rev := ctx.Args().Get(0)
		name := ctx.Args().Get(1)

		if err := ctl.Tag(rev, name); err != nil {
			return ExitCode{
				UnknownError,
				fmt.Sprintf("tag: %v", err),
			}
		}
	}

	return nil
}

func handleLog(ctx *cli.Context, ctl *client.Client) error {
	entries, err := ctl.Log()
	if err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("commit: %v", err)}
	}

	for _, entry := range entries {
		tags := ""
		if len(entry.Tags) > 0 {
			tags = fmt.Sprintf(" (%s)", strings.Join(entry.Tags, ", "))
		}

		msg := entry.Msg
		if msg == "" {
			msg = color.RedString("•")
		}

		entry.Hash.ShortB58()

		fmt.Printf(
			"%s %s %s%s\n",
			color.GreenString(entry.Hash.ShortB58()),
			color.YellowString(entry.Date.Format(time.Stamp)),
			msg,
			color.CyanString(tags),
		)
	}

	return nil
}
<commit_msg>cmd: redefine diff usage<commit_after>package cmd

import (
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/sahib/brig/cmd/tabwriter"

	"github.com/fatih/color"
	"github.com/sahib/brig/client"
	"github.com/urfave/cli"
)

func handleReset(ctx *cli.Context, ctl *client.Client) error {
	force := ctx.Bool("force")
	path := ctx.Args().First()
	rev := "HEAD"

	if len(ctx.Args()) > 1 {
		rev = ctx.Args().Get(1)
	}

	if err := ctl.Reset(path, rev, force); err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("unpin: %v", err)}
	}

	return nil
}

func commitName(cmt *client.Commit) string {
	if len(cmt.Tags) > 0 {
		return strings.ToUpper(cmt.Tags[0])
	}

	return cmt.Hash.ShortB58()
}

func handleHistory(ctx *cli.Context, ctl *client.Client) error {
	path := ctx.Args().First()

	history, err := ctl.History(path)
	if err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("history: %v", err)}
	}

	if _, err := ctl.Stat(path); err != nil {
		fmt.Printf("%s %s",
			color.YellowString("WARNING:"),
			`This file is not part of this commit, but there's still history for it.
         Most likely this file was moved or removed in the past.

`)
	}

	tabW := tabwriter.NewWriter(
		os.Stdout, 0, 0, 2, ' ',
		tabwriter.StripEscape,
	)

	if len(history) != 0 {
		fmt.Fprintf(tabW, "CHANGE\tFROM\tTO\tHOW\tWHEN\t\n")
	}

	for idx, entry := range history {
		what := ""
		printLine := true

		for _, detail := range entry.Mask {
			// If it was moved, let's display what moved.
			if detail == "moved" && idx+1 < len(history) {
				src := history[idx+1].Path
				dst := entry.Path

				if entry.ReferTo != "" {
					dst = entry.ReferTo
				}

				what = fmt.Sprintf(
					"%s → %s", color.RedString(src), color.RedString(dst),
				)
			}

			// Only display empty changes if nothing happened.
			if detail == "none" && !ctx.Bool("empty") {
				printLine = false
			}
		}
		if !printLine {
			continue
		}

		changeDesc := color.YellowString(strings.Join(entry.Mask, ", "))
		when := color.MagentaString(entry.Head.Date.Format(time.Stamp))

		fmt.Fprintf(
			tabW,
			"%s\t%s\t%s\t%s\t%s\t\n",
			changeDesc,
			color.CyanString(commitName(entry.Next)),
			color.GreenString(commitName(entry.Head)),
			what,
			when,
		)
	}

	return tabW.Flush()
}

func printDiffTree(diff *client.Diff) {
	const (
		diffTypeNone = iota
		diffTypeAdded
		diffTypeRemoved
		diffTypeIgnored
		diffTypeConflict
		diffTypeMerged
	)

	type diffEntry struct {
		typ  int
		pair client.DiffPair
	}

	entries := []client.StatInfo{}
	types := make(map[string]diffEntry)

	// Singular types:
	for _, info := range diff.Added {
		types[info.Path] = diffEntry{typ: diffTypeAdded}
		entries = append(entries, info)
	}
	for _, info := range diff.Removed {
		types[info.Path] = diffEntry{typ: diffTypeRemoved}
		entries = append(entries, info)
	}
	for _, info := range diff.Ignored {
		types[info.Path] = diffEntry{typ: diffTypeIgnored}
		entries = append(entries, info)
	}

	// Pair types:
	for _, pair := range diff.Conflict {
		types[pair.Dst.Path] = diffEntry{
			typ:  diffTypeConflict,
			pair: pair,
		}
		entries = append(entries, pair.Dst)
	}
	for _, pair := range diff.Merged {
		types[pair.Dst.Path] = diffEntry{
			typ:  diffTypeMerged,
			pair: pair,
		}
		entries = append(entries, pair.Dst)
	}

	if len(entries) == 0 {
		// Nothing to show:
		return
	}

	// Called to format each name in the resulting tree:
	formatter := func(n *treeNode) string {
		if n.name == "/" {
			return color.MagentaString("•")
		}

		if diffEntry, ok := types[n.entry.Path]; ok {
			switch diffEntry.typ {
			case diffTypeAdded:
				return color.GreenString(" + " + n.name)
			case diffTypeRemoved:
				return color.RedString(" - " + n.name)
			case diffTypeIgnored:
				return color.YellowString(" * " + n.name)
			case diffTypeMerged:
				name := fmt.Sprintf(
					" %s ⇄ %s",
					diffEntry.pair.Src.Path,
					diffEntry.pair.Dst.Path,
				)
				return color.CyanString(name)
			case diffTypeConflict:
				name := fmt.Sprintf(
					" %s ⚡ %s",
					diffEntry.pair.Src.Path,
					diffEntry.pair.Dst.Path,
				)
				return color.MagentaString(name)
			}
		}

		return n.name
	}

	// Render the tree:
	showTree(entries, &treeCfg{
		format:  formatter,
		showPin: false,
	})
}

func printDiff(diff *client.Diff) {
	simpleSection := func(heading string, infos []client.StatInfo) {
		if len(infos) == 0 {
			return
		}

		fmt.Println(heading)
		for _, info := range infos {
			fmt.Printf("  %s\n", info.Path)
		}

		fmt.Println()
	}

	pairSection := func(heading string, infos []client.DiffPair) {
		if len(infos) == 0 {
			return
		}

		for _, pair := range diff.Merged {
			fmt.Printf("  %s <-> %s\n", pair.Src.Path, pair.Dst.Path)
		}

		fmt.Println()
	}

	simpleSection(color.GreenString("Added:"), diff.Added)
	simpleSection(color.YellowString("Ignored:"), diff.Ignored)
	simpleSection(color.RedString("Removed:"), diff.Removed)

	pairSection(color.CyanString("Resolveable Conflicts:"), diff.Merged)
	pairSection(color.MagentaString("Conflicts:"), diff.Conflict)
}

func handleDiff(ctx *cli.Context, ctl *client.Client) error {
	if ctx.NArg() > 4 {
		fmt.Println("More than four arguments can't be handled.")
	}

	self, err := ctl.Whoami()
	if err != nil {
		return err
	}

	localName := self.CurrentUser
	remoteName := self.CurrentUser

	localRev := "HEAD"
	remoteRev := "HEAD"

	switch n := ctx.NArg(); n {
	case 1:
		remoteRev = ctx.Args().Get(0)
	case 2:
		localRev = ctx.Args().Get(0)
		remoteRev = ctx.Args().Get(1)
	case 3:
		remoteName = ctx.Args().Get(0)
		localRev = ctx.Args().Get(1)
		remoteRev = ctx.Args().Get(2)
	case 4:
		localName = ctx.Args().Get(0)
		remoteName = ctx.Args().Get(1)
		localRev = ctx.Args().Get(2)
		remoteRev = ctx.Args().Get(3)
	}

	diff, err := ctl.MakeDiff(localName, remoteName, localRev, remoteRev)
	if err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("diff: %v", err)}
	}

	if ctx.Bool("list") {
		printDiff(diff)
	} else {
		printDiffTree(diff)
	}

	return nil
}

func handleFetch(ctx *cli.Context, ctl *client.Client) error {
	who := ctx.Args().First()
	return ctl.Fetch(who)
}

func handleSync(ctx *cli.Context, ctl *client.Client) error {
	who := ctx.Args().First()

	needFetch := true
	if ctx.Bool("no-fetch") {
		needFetch = false
	}

	return ctl.Sync(who, needFetch)
}

func handleStatus(ctx *cli.Context, ctl *client.Client) error {
	self, err := ctl.Whoami()
	if err != nil {
		return err
	}

	curr := self.CurrentUser
	diff, err := ctl.MakeDiff(curr, curr, "HEAD", "CURR")
	if err != nil {
		return err
	}

	if ctx.Bool("tree") {
		printDiffTree(diff)
	} else {
		printDiff(diff)
	}

	return nil
}

func handleBecome(ctx *cli.Context, ctl *client.Client) error {
	becomeSelf := ctx.Bool("self")
	if !becomeSelf && ctx.NArg() < 1 {
		return fmt.Errorf("become needs at least one argument without -s")
	}

	whoami, err := ctl.Whoami()
	if err != nil {
		return err
	}

	who := ctx.Args().First()
	if becomeSelf {
		who = whoami.Owner
	}

	if whoami.CurrentUser == who {
		fmt.Printf("You are already %s.\n", color.GreenString(who))
		return nil
	}

	if err := ctl.Become(who); err != nil {
		return err
	}

	suffix := "Changes will be local only."
	if who == whoami.Owner {
		suffix = "Welcome back!"
	}

	fmt.Printf(
		"You are viewing %s's data now. %s\n",
		color.GreenString(who), suffix,
	)
	return nil
}

func handleCommit(ctx *cli.Context, ctl *client.Client) error {
	msg := ""
	if msg = ctx.String("message"); msg == "" {
		msg = fmt.Sprintf("manual commit")
	}

	if err := ctl.MakeCommit(msg); err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("commit: %v", err)}
	}

	return nil
}

func handleTag(ctx *cli.Context, ctl *client.Client) error {
	if ctx.Bool("delete") {
		name := ctx.Args().Get(0)

		if err := ctl.Untag(name); err != nil {
			return ExitCode{
				UnknownError,
				fmt.Sprintf("untag: %v", err),
			}
		}
	} else {
		if len(ctx.Args()) < 2 {
			return ExitCode{BadArgs, "tag needs at least two arguments"}
		}

		rev := ctx.Args().Get(0)
		name := ctx.Args().Get(1)

		if err := ctl.Tag(rev, name); err != nil {
			return ExitCode{
				UnknownError,
				fmt.Sprintf("tag: %v", err),
			}
		}
	}

	return nil
}

func handleLog(ctx *cli.Context, ctl *client.Client) error {
	entries, err := ctl.Log()
	if err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("commit: %v", err)}
	}

	for _, entry := range entries {
		tags := ""
		if len(entry.Tags) > 0 {
			tags = fmt.Sprintf(" (%s)", strings.Join(entry.Tags, ", "))
		}

		msg := entry.Msg
		if msg == "" {
			msg = color.RedString("•")
		}

		entry.Hash.ShortB58()

		fmt.Printf(
			"%s %s %s%s\n",
			color.GreenString(entry.Hash.ShortB58()),
			color.YellowString(entry.Date.Format(time.Stamp)),
			msg,
			color.CyanString(tags),
		)
	}

	return nil
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"log"
	"strings"
)

func InstallCommonHelpers(root *CmdNode) {

	cmdNone := CMD_NONE
	//cmdConf := CMD_CONF

	CmdInstall(root, cmdNone, "commit", CONF, cmdCommit, "Apply current candidate configuration")
	CmdInstall(root, cmdNone, "configure", ENAB, cmdConfig, "Enter configuration mode")
	CmdInstall(root, cmdNone, "enable", EXEC, cmdEnable, "Enter privileged mode")
	CmdInstall(root, cmdNone, "exit", EXEC, cmdExit, "Exit current location")
	CmdInstall(root, cmdNone, "list", EXEC, cmdList, "List command tree")
	CmdInstall(root, cmdNone, "no {ANY}", CONF, HelperNo, "Remove a configuration item")
	CmdInstall(root, cmdNone, "quit", EXEC, cmdQuit, "Quit session")
	CmdInstall(root, cmdNone, "reload", ENAB, cmdReload, "Reload")
	CmdInstall(root, cmdNone, "rollback", CONF, cmdRollback, "Reset candidate configuration from active configuration")
	CmdInstall(root, cmdNone, "rollback {ID}", CONF, cmdRollback, "Reset candidate configuration from rollback configuration")
	CmdInstall(root, cmdNone, "show configuration", EXEC, cmdShowConf, "Show candidate configuration")
	CmdInstall(root, cmdNone, "show configuration line-mode", EXEC, cmdShowConf, "Show candidate configuration in line-mode")
	CmdInstall(root, cmdNone, "show history", EXEC, cmdShowHistory, "Show command history")
	CmdInstall(root, cmdNone, "show running-configuration", EXEC, cmdShowRun, "Show active configuration")
}

func cmdCommit(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	// get diff from active conf to candidate conf
	// build command list to apply diff to active conf
	//  - include preparatory commands, like deleting addresses from interfaces affected by address change
	//  - if any command fails, revert previously applied commands
	// save new active conf with new commit id
}

func cmdConfig(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	status := c.Status()
	if status < CONF {
		c.StatusConf()
	}
}

func cmdEnable(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	status := c.Status()
	if status < ENAB {
		c.StatusEnable()
	}
}

func cmdExit(ctx ConfContext, node *CmdNode, line string, c CmdClient) {

	path := c.ConfigPath()
	if path == "" {
		if c.Status() <= EXEC {
			c.Sendln("use 'quit' to exit remote terminal")
			return
		}
		c.StatusExit()
		return
	}

	fields := strings.Fields(path)
	newPath := strings.Join(fields[:len(fields)-1], " ")

	c.ConfigPathSet(newPath)
}

func cmdQuit(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	c.SendlnNow("")
	c.SendlnNow("bye")
	log.Printf("cmdQuit: requesting intputLoop to quit")
	c.InputQuit()
}

func cmdReload(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
}

func cmdRollback(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	fields := strings.Fields(line)
	if len(fields) > 1 {
		id := fields[1]
		log.Printf("cmdRollback: reset candidate config from rollback: %s", id)
	} else {
		log.Printf("cmdRollback: reset candidate config from active configuration")
	}
}

func cmdShowConf(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	showConfig(ctx.ConfRootCandidate(), node, line, c, "candidate configuration:")
}

func showConfig(root *ConfNode, node *CmdNode, line string, c CmdClient, head string) {
	fields := strings.Fields(line)
	lineMode := len(fields) > 2 && strings.HasPrefix("line-mode", fields[2])
	c.Sendln(head)
	ShowConf(root, node, c, lineMode)
}

func cmdShowHistory(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	c.Sendln("command history:")
	c.HistoryShow()
}

func cmdShowRun(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	showConfig(ctx.ConfRootActive(), node, line, c, "running configuration:")
}

// Iface addr config should not be a helper function,
// since it only applies to RIB daemon.
// However it is currently being used for helping in tests.
func HelperIfaceAddr(ctx ConfContext, node *CmdNode, line string, c CmdClient) {

	linePath, addr := StripLastToken(line)
	log.Printf("cmdIfaceAddr: FIXME check IPv4/plen syntax: ipv4=%s", addr)

	path, _ := StripLastToken(node.Path)

	confCand := ctx.ConfRootCandidate()
	confNode, err, _ := confCand.Set(path, linePath)
	if err != nil {
		log.Printf("iface addr: error: %v", err)
		return
	}

	confNode.ValueAdd(addr)
}

func HelperDescription(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	// line: "interf  XXXX   descrip   YYY ZZZ WWW"
	//                                 ^^^^^^^^^^^

	// find 3rd space
	ln := strings.TrimLeft(line, " ") // drop leading spaces

	i := IndexByte(ln, ' ', 3)
	if i < 0 {
		c.Sendln(fmt.Sprintf("cmdDescr: could not find description argument: [%s]", line))
		return
	}

	desc := ln[i+1:]

	lineFields := strings.Fields(line)
	linePath := strings.Join(lineFields[:3], " ")

	fields := strings.Fields(node.Path)
	path := strings.Join(fields[:3], " ") // interface XXX description

	confCand := ctx.ConfRootCandidate()
	confNode, err, _ := confCand.Set(path, linePath)
	if err != nil {
		log.Printf("description: error: %v", err)
		return
	}

	confNode.ValueSet(desc)
}

func HelperHostname(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	line, host := StripLastToken(line)

	path, _ := StripLastToken(node.Path)

	confCand := ctx.ConfRootCandidate()
	confNode, err, _ := confCand.Set(path, line)
	if err != nil {
		log.Printf("hostname: error: %v", err)
		return
	}

	confNode.ValueSet(host)
}

func cmdList(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	list(ctx.CmdRoot(), 0, c)
}

func list(node *CmdNode, depth int, c CmdClient) {
	handler := "----"
	if node.Handler != nil {
		handler = "LEAF"
	}
	ident := strings.Repeat(" ", 4*depth)
	output := fmt.Sprintf("%s %d %s[%s] desc=[%s]", handler, node.MinLevel, ident, node.Path, node.Desc)
	//log.Printf(output)
	c.Sendln(output)
	for _, n := range node.Children {
		list(n, depth+1, c)
	}
}

func HelperNo(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	c.Sendln(fmt.Sprintf("cmdNo: [%s]", line))

	sep := strings.IndexByte(line, ' ')
	if sep < 0 {
		c.Sendln(fmt.Sprintf("cmdNo: missing argument: %v", line))
		return
	}

	arg := line[sep:]

	//cc := c.(*cli.Client)
	//status := cc.Status()
	status := c.Status()

	node, _, err := CmdFindRelative(ctx.CmdRoot(), arg, c.ConfigPath(), status)
	if err != nil {
		c.Sendln(fmt.Sprintf("cmdNo: not found [%s]: %v", arg, err))
		return
	}

	if !node.IsConfig() {
		c.Sendln(fmt.Sprintf("cmdNo: not a configuration command: [%s]", arg))
		return
	}

	matchAny := node.MatchAny()
	childMatchAny := !matchAny && len(node.Children) == 1 && node.Children[0].MatchAny()

	c.Sendln(fmt.Sprintf("cmdNo: [%s] len=%d matchAny=%v childMatchAny=%v", node.Path, len(strings.Fields(node.Path)), matchAny, childMatchAny))

	expanded, e := CmdExpand(arg, node.Path)
	if e != nil {
		c.Sendln(fmt.Sprintf("cmdNo: could not expand arg=[%s] cmd=[%s]: %v", arg, node.Path, e))
		return
	}

	var parentConf *ConfNode
	var childIndex int

	switch {
	case matchAny:
		// arg,node.Path is child: ... parent child value

		parentPath, childLabel := StripLastToken(expanded)
		parentPath, childLabel = StripLastToken(parentPath)

		parentConf, e = ctx.ConfRootCandidate().Get(parentPath)
		if e != nil {
			c.Sendln(fmt.Sprintf("cmdNo: config parent node not found [%s]: %v", parentPath, e))
			return
		}

		childIndex = parentConf.FindChild(childLabel)

	case childMatchAny:
		// arg,node.Path is parent of single child: ... parent child value

		parentPath, childLabel := StripLastToken(expanded)

		parentConf, e = ctx.ConfRootCandidate().Get(parentPath)
		if e != nil {
			c.Sendln(fmt.Sprintf("cmdNo: config parent node not found [%s]: %v", parentPath, e))
			return
		}

		childIndex = parentConf.FindChild(childLabel)

	default:
		// arg,node.Path is one of: intermediate node, leaf node, value of single-value leaf node, value of multi-value leaf node

		parentPath, childLabel := StripLastToken(expanded)

		parentConf, e = ctx.ConfRootCandidate().Get(parentPath)
		if e != nil {
			c.Sendln(fmt.Sprintf("cmdNo: config parent node not found [%s]: %v", parentPath, e))
			return
		}

		childIndex = parentConf.FindChild(childLabel)

		_, cmdLast := StripLastToken(node.Path)
		if IsConfigValueKeyword(cmdLast) {
			if e2 := parentConf.ValueDelete(childLabel); e2 != nil {
				c.Sendln(fmt.Sprintf("cmdNo: could not delete value: %v", e2))
				return
			}

			if len(parentConf.Value) > 0 {
				return // done, can't delete node
			}

			// node without value

			parentPath, childLabel = StripLastToken(parentPath)

			parentConf, e = ctx.ConfRootCandidate().Get(parentPath)
			if e != nil {
				c.Sendln(fmt.Sprintf("cmdNo: config parent node not found [%s]: %v", parentPath, e))
				return
			}

			childIndex = parentConf.FindChild(childLabel)
		}
	}

	c.Sendln(fmt.Sprintf("cmdNo: parent=[%s] childIndex=%d", parentConf.Path, childIndex))
	c.Sendln(fmt.Sprintf("cmdNo: parent=[%s] child=[%s]", parentConf.Path, parentConf.Children[childIndex].Path))

	ctx.ConfRootCandidate().Prune(parentConf, parentConf.Children[childIndex], c)
}
<commit_msg>Skeleton for atomic commits.<commit_after>package command

import (
	"fmt"
	"log"
	"strings"
)

func InstallCommonHelpers(root *CmdNode) {

	cmdNone := CMD_NONE
	//cmdConf := CMD_CONF

	CmdInstall(root, cmdNone, "commit", CONF, cmdCommit, "Apply current candidate configuration")
	CmdInstall(root, cmdNone, "configure", ENAB, cmdConfig, "Enter configuration mode")
	CmdInstall(root, cmdNone, "enable", EXEC, cmdEnable, "Enter privileged mode")
	CmdInstall(root, cmdNone, "exit", EXEC, cmdExit, "Exit current location")
	CmdInstall(root, cmdNone, "list", EXEC, cmdList, "List command tree")
	CmdInstall(root, cmdNone, "no {ANY}", CONF, HelperNo, "Remove a configuration item")
	CmdInstall(root, cmdNone, "quit", EXEC, cmdQuit, "Quit session")
	CmdInstall(root, cmdNone, "reload", ENAB, cmdReload, "Reload")
	CmdInstall(root, cmdNone, "rollback", CONF, cmdRollback, "Reset candidate configuration from active configuration")
	CmdInstall(root, cmdNone, "rollback {ID}", CONF, cmdRollback, "Reset candidate configuration from rollback configuration")
	CmdInstall(root, cmdNone, "show configuration", EXEC, cmdShowConf, "Show candidate configuration")
	CmdInstall(root, cmdNone, "show configuration line-mode", EXEC, cmdShowConf, "Show candidate configuration in line-mode")
	CmdInstall(root, cmdNone, "show history", EXEC, cmdShowHistory, "Show command history")
	CmdInstall(root, cmdNone, "show running-configuration", EXEC, cmdShowRun, "Show active configuration")
}

func cmdCommit(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	// get diff from active conf to candidate conf
	// build command list to apply diff to active conf
	//  - include preparatory commands, like deleting addresses from interfaces affected by address change
	//  - if any command fails, revert previously applied commands
	// save new active conf with new commit id

	confOld := ctx.ConfRootActive()
	confNew := ctx.ConfRootCandidate()
	cmdList := diff(confOld, confNew)
	for _, conf := range cmdList {
		c.Sendln(fmt.Sprintf("commit: %s", conf))
	}
}

func diff(root1, root2 *ConfNode) []string {
	list := []string{"command1", "command2"}
	return list
}

func cmdConfig(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	status := c.Status()
	if status < CONF {
		c.StatusConf()
	}
}

func cmdEnable(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	status := c.Status()
	if status < ENAB {
		c.StatusEnable()
	}
}

func cmdExit(ctx ConfContext, node *CmdNode, line string, c CmdClient) {

	path := c.ConfigPath()
	if path == "" {
		if c.Status() <= EXEC {
			c.Sendln("use 'quit' to exit remote terminal")
			return
		}
		c.StatusExit()
		return
	}

	fields := strings.Fields(path)
	newPath := strings.Join(fields[:len(fields)-1], " ")

	c.ConfigPathSet(newPath)
}

func cmdQuit(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	c.SendlnNow("")
	c.SendlnNow("bye")
	log.Printf("cmdQuit: requesting intputLoop to quit")
	c.InputQuit()
}

func cmdReload(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
}

func cmdRollback(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	fields := strings.Fields(line)
	if len(fields) > 1 {
		id := fields[1]
		log.Printf("cmdRollback: reset candidate config from rollback: %s", id)
	} else {
		log.Printf("cmdRollback: reset candidate config from active configuration")
	}
}

func cmdShowConf(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	showConfig(ctx.ConfRootCandidate(), node, line, c, "candidate configuration:")
}

func showConfig(root *ConfNode, node *CmdNode, line string, c CmdClient, head string) {
	fields := strings.Fields(line)
	lineMode := len(fields) > 2 && strings.HasPrefix("line-mode", fields[2])
	c.Sendln(head)
	ShowConf(root, node, c, lineMode)
}

func cmdShowHistory(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	c.Sendln("command history:")
	c.HistoryShow()
}

func cmdShowRun(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	showConfig(ctx.ConfRootActive(), node, line, c, "running configuration:")
}

// Iface addr config should not be a helper function,
// since it only applies to RIB daemon.
// However it is currently being used for helping in tests.
func HelperIfaceAddr(ctx ConfContext, node *CmdNode, line string, c CmdClient) {

	linePath, addr := StripLastToken(line)
	log.Printf("cmdIfaceAddr: FIXME check IPv4/plen syntax: ipv4=%s", addr)

	path, _ := StripLastToken(node.Path)

	confCand := ctx.ConfRootCandidate()
	confNode, err, _ := confCand.Set(path, linePath)
	if err != nil {
		log.Printf("iface addr: error: %v", err)
		return
	}

	confNode.ValueAdd(addr)
}

func HelperDescription(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	// line: "interf  XXXX   descrip   YYY ZZZ WWW"
	//                                 ^^^^^^^^^^^

	// find 3rd space
	ln := strings.TrimLeft(line, " ") // drop leading spaces

	i := IndexByte(ln, ' ', 3)
	if i < 0 {
		c.Sendln(fmt.Sprintf("cmdDescr: could not find description argument: [%s]", line))
		return
	}

	desc := ln[i+1:]

	lineFields := strings.Fields(line)
	linePath := strings.Join(lineFields[:3], " ")

	fields := strings.Fields(node.Path)
	path := strings.Join(fields[:3], " ") // interface XXX description

	confCand := ctx.ConfRootCandidate()
	confNode, err, _ := confCand.Set(path, linePath)
	if err != nil {
		log.Printf("description: error: %v", err)
		return
	}

	confNode.ValueSet(desc)
}

func HelperHostname(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	line, host := StripLastToken(line)

	path, _ := StripLastToken(node.Path)

	confCand := ctx.ConfRootCandidate()
	confNode, err, _ := confCand.Set(path, line)
	if err != nil {
		log.Printf("hostname: error: %v", err)
		return
	}

	confNode.ValueSet(host)
}

func cmdList(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	list(ctx.CmdRoot(), 0, c)
}

func list(node *CmdNode, depth int, c CmdClient) {
	handler := "----"
	if node.Handler != nil {
		handler = "LEAF"
	}
	ident := strings.Repeat(" ", 4*depth)
	output := fmt.Sprintf("%s %d %s[%s] desc=[%s]", handler, node.MinLevel, ident, node.Path, node.Desc)
	//log.Printf(output)
	c.Sendln(output)
	for _, n := range node.Children {
		list(n, depth+1, c)
	}
}

func HelperNo(ctx ConfContext, node *CmdNode, line string, c CmdClient) {
	c.Sendln(fmt.Sprintf("cmdNo: [%s]", line))

	sep := strings.IndexByte(line, ' ')
	if sep < 0 {
		c.Sendln(fmt.Sprintf("cmdNo: missing argument: %v", line))
		return
	}

	arg := line[sep:]

	//cc := c.(*cli.Client)
	//status := cc.Status()
	status := c.Status()

	node, _, err := CmdFindRelative(ctx.CmdRoot(), arg, c.ConfigPath(), status)
	if err != nil {
		c.Sendln(fmt.Sprintf("cmdNo: not found [%s]: %v", arg, err))
		return
	}

	if !node.IsConfig() {
		c.Sendln(fmt.Sprintf("cmdNo: not a configuration command: [%s]", arg))
		return
	}

	matchAny := node.MatchAny()
	childMatchAny := !matchAny && len(node.Children) == 1 && node.Children[0].MatchAny()

	c.Sendln(fmt.Sprintf("cmdNo: [%s] len=%d matchAny=%v childMatchAny=%v", node.Path, len(strings.Fields(node.Path)), matchAny, childMatchAny))

	expanded, e := CmdExpand(arg, node.Path)
	if e != nil {
		c.Sendln(fmt.Sprintf("cmdNo: could not expand arg=[%s] cmd=[%s]: %v", arg, node.Path, e))
		return
	}

	var parentConf *ConfNode
	var childIndex int

	switch {
	case matchAny:
		// arg,node.Path is child: ... parent child value

		parentPath, childLabel := StripLastToken(expanded)
		parentPath, childLabel = StripLastToken(parentPath)

		parentConf, e = ctx.ConfRootCandidate().Get(parentPath)
		if e != nil {
			c.Sendln(fmt.Sprintf("cmdNo: config parent node not found [%s]: %v", parentPath, e))
			return
		}

		childIndex = parentConf.FindChild(childLabel)

	case childMatchAny:
		// arg,node.Path is parent of single child: ... parent child value

		parentPath, childLabel := StripLastToken(expanded)

		parentConf, e = ctx.ConfRootCandidate().Get(parentPath)
		if e != nil {
			c.Sendln(fmt.Sprintf("cmdNo: config parent node not found [%s]: %v", parentPath, e))
			return
		}

		childIndex = parentConf.FindChild(childLabel)

	default:
		// arg,node.Path is one of: intermediate node, leaf node, value of single-value leaf node, value of multi-value leaf node

		parentPath, childLabel := StripLastToken(expanded)

		parentConf, e = ctx.ConfRootCandidate().Get(parentPath)
		if e != nil {
			c.Sendln(fmt.Sprintf("cmdNo: config parent node not found [%s]: %v", parentPath, e))
			return
		}

		childIndex = parentConf.FindChild(childLabel)

		_, cmdLast := StripLastToken(node.Path)
		if IsConfigValueKeyword(cmdLast) {
			if e2 := parentConf.ValueDelete(childLabel); e2 != nil {
				c.Sendln(fmt.Sprintf("cmdNo: could not delete value: %v", e2))
				return
			}

			if len(parentConf.Value) > 0 {
				return // done, can't delete node
			}

			// node without value

			parentPath, childLabel = StripLastToken(parentPath)

			parentConf, e = ctx.ConfRootCandidate().Get(parentPath)
			if e != nil {
				c.Sendln(fmt.Sprintf("cmdNo: config parent node not found [%s]: %v", parentPath, e))
				return
			}

			childIndex = parentConf.FindChild(childLabel)
		}
	}

	c.Sendln(fmt.Sprintf("cmdNo: parent=[%s] childIndex=%d", parentConf.Path, childIndex))
	c.Sendln(fmt.Sprintf("cmdNo: parent=[%s] child=[%s]", parentConf.Path, parentConf.Children[childIndex].Path))

	ctx.ConfRootCandidate().Prune(parentConf, parentConf.Children[childIndex], c)
}
<|endoftext|>