<commit_before>/*
Copyright 2017 Google Inc. All rights reserved.
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.
*/

// The tika command provides a command line interface for Tika Server.
package main

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strings"

	"github.com/google/go-tika/tika"
)

func usage() {
	fmt.Printf("Usage: %s [OPTIONS] ACTION\n\n", os.Args[0])
	fmt.Printf("ACTIONS: parse, detect, language, meta, version, parsers, mimetypes, detectors\n\n")
	fmt.Println("OPTIONS:")
	flag.PrintDefaults()
}

// Flags requiring input.
const (
	parse    = "parse"
	detect   = "detect"
	language = "language"
	meta     = "meta"
)

// Informational flags which don't require input.
const (
	version   = "version"
	parsers   = "parsers"
	mimeTypes = "mimetypes"
	detectors = "detectors"
)

// Command line flags.
var (
	downloadVersion = flag.String("download_version", "", "Tika Server JAR version to download. If -serverJAR is specified, it will be downloaded to that location, otherwise it will be downloaded to your working directory. If the JAR has already been downloaded and has the correct MD5, this will do nothing. Valid versions: 1.14.")
	filename        = flag.String("filename", "", "Path to file to parse.")
	metaField       = flag.String("field", "", `Specific field to get when using the "meta" action. Undefined when using the -recursive flag.`)
	recursive       = flag.Bool("recursive", false, `Whether to run "parse" or "meta" recursively, returning a list with one element per embedded document. Undefined when using the -field flag.`)
	serverJAR       = flag.String("server_jar", "", "Absolute path to the Tika Server JAR. This will start a new server, ignoring -serverURL.")
	serverURL       = flag.String("server_url", "", "URL of Tika server.")
)

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() != 1 {
		flag.Usage()
		os.Exit(1)
	}
	action := flag.Arg(0)

	if *downloadVersion != "" {
		if *serverJAR == "" {
			*serverJAR = "tika-server-" + *downloadVersion + ".jar"
		}
		err := tika.DownloadServer(context.Background(), tika.Version(*downloadVersion), *serverJAR)
		if err != nil {
			log.Fatal(err)
		}
	}

	if *serverURL == "" && *serverJAR == "" {
		log.Fatal("no URL specified: set serverURL, serverJAR and/or downloadVersion")
	}

	var cancel func()
	if *serverJAR != "" {
		s, err := tika.NewServer(*serverJAR)
		if err != nil {
			log.Fatal(err)
		}

		cancel, err = s.Start(context.Background())
		if err != nil {
			log.Fatalf("could not start server: %v", err)
		}
		defer cancel()

		*serverURL = s.URL()
	}

	var file io.Reader

	// Check actions requiring input have an input and get it.
	switch action {
	case parse, detect, language, meta:
		if *filename == "" {
			cancel()
			log.Fatalf("error: you must provide an input filename")
		}
		var err error
		file, err = os.Open(*filename)
		if err != nil {
			cancel()
			log.Fatalf("error opening file: %v", err)
		}
	}

	c := tika.NewClient(nil, *serverURL)
	b, err := process(c, action, file)
	if err != nil {
		cancel()
		log.Fatalf("tika error: %v", err)
	}
	fmt.Println(b)
}

func process(c *tika.Client, action string, file io.Reader) (string, error) {
	switch action {
	default:
		flag.Usage()
		return "", fmt.Errorf("error: invalid action %q", action)
	case parse:
		if *recursive {
			bs, err := c.ParseRecursive(context.Background(), file)
			return strings.Join(bs, "\n"), err
		}
		return c.Parse(context.Background(), file)
	case detect:
		return c.Detect(context.Background(), file)
	case language:
		return c.Language(context.Background(), file)
	case meta:
		if *metaField != "" {
			return c.MetaField(context.Background(), file, *metaField)
		} else if *recursive {
			mr, err := c.MetaRecursive(context.Background(), file)
			if err != nil {
				return "", err
			}
			bytes, err := json.MarshalIndent(mr, "", "  ")
			if err != nil {
				return "", err
			}
			return string(bytes), nil
		}
		return c.Meta(context.Background(), file)
	case version:
		return c.Version(context.Background())
	case parsers:
		p, err := c.Parsers(context.Background())
		if err != nil {
			return "", err
		}
		bytes, err := json.MarshalIndent(p, "", "  ")
		if err != nil {
			return "", err
		}
		return string(bytes), nil
	case mimeTypes:
		mt, err := c.MIMETypes(context.Background())
		if err != nil {
			return "", err
		}
		bytes, err := json.MarshalIndent(mt, "", "  ")
		if err != nil {
			return "", err
		}
		return string(bytes), nil
	case detectors:
		d, err := c.Detectors(context.Background())
		if err != nil {
			return "", err
		}
		bytes, err := json.MarshalIndent(d, "", "  ")
		if err != nil {
			return "", err
		}
		return string(bytes), nil
	}
}
<commit_msg>Drop yet another else after a return<commit_after>/*
Copyright 2017 Google Inc. All rights reserved.
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.
*/

// The tika command provides a command line interface for Tika Server.
package main

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strings"

	"github.com/google/go-tika/tika"
)

func usage() {
	fmt.Printf("Usage: %s [OPTIONS] ACTION\n\n", os.Args[0])
	fmt.Printf("ACTIONS: parse, detect, language, meta, version, parsers, mimetypes, detectors\n\n")
	fmt.Println("OPTIONS:")
	flag.PrintDefaults()
}

// Flags requiring input.
const (
	parse    = "parse"
	detect   = "detect"
	language = "language"
	meta     = "meta"
)

// Informational flags which don't require input.
const (
	version   = "version"
	parsers   = "parsers"
	mimeTypes = "mimetypes"
	detectors = "detectors"
)

// Command line flags.
var (
	downloadVersion = flag.String("download_version", "", "Tika Server JAR version to download. If -serverJAR is specified, it will be downloaded to that location, otherwise it will be downloaded to your working directory. If the JAR has already been downloaded and has the correct MD5, this will do nothing. Valid versions: 1.14.")
	filename        = flag.String("filename", "", "Path to file to parse.")
	metaField       = flag.String("field", "", `Specific field to get when using the "meta" action. Undefined when using the -recursive flag.`)
	recursive       = flag.Bool("recursive", false, `Whether to run "parse" or "meta" recursively, returning a list with one element per embedded document. Undefined when using the -field flag.`)
	serverJAR       = flag.String("server_jar", "", "Absolute path to the Tika Server JAR. This will start a new server, ignoring -serverURL.")
	serverURL       = flag.String("server_url", "", "URL of Tika server.")
)

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() != 1 {
		flag.Usage()
		os.Exit(1)
	}
	action := flag.Arg(0)

	if *downloadVersion != "" {
		if *serverJAR == "" {
			*serverJAR = "tika-server-" + *downloadVersion + ".jar"
		}
		err := tika.DownloadServer(context.Background(), tika.Version(*downloadVersion), *serverJAR)
		if err != nil {
			log.Fatal(err)
		}
	}

	if *serverURL == "" && *serverJAR == "" {
		log.Fatal("no URL specified: set serverURL, serverJAR and/or downloadVersion")
	}

	var cancel func()
	if *serverJAR != "" {
		s, err := tika.NewServer(*serverJAR)
		if err != nil {
			log.Fatal(err)
		}

		cancel, err = s.Start(context.Background())
		if err != nil {
			log.Fatalf("could not start server: %v", err)
		}
		defer cancel()

		*serverURL = s.URL()
	}

	var file io.Reader

	// Check actions requiring input have an input and get it.
	switch action {
	case parse, detect, language, meta:
		if *filename == "" {
			cancel()
			log.Fatalf("error: you must provide an input filename")
		}
		var err error
		file, err = os.Open(*filename)
		if err != nil {
			cancel()
			log.Fatalf("error opening file: %v", err)
		}
	}

	c := tika.NewClient(nil, *serverURL)
	b, err := process(c, action, file)
	if err != nil {
		cancel()
		log.Fatalf("tika error: %v", err)
	}
	fmt.Println(b)
}

func process(c *tika.Client, action string, file io.Reader) (string, error) {
	switch action {
	default:
		flag.Usage()
		return "", fmt.Errorf("error: invalid action %q", action)
	case parse:
		if *recursive {
			bs, err := c.ParseRecursive(context.Background(), file)
			return strings.Join(bs, "\n"), err
		}
		return c.Parse(context.Background(), file)
	case detect:
		return c.Detect(context.Background(), file)
	case language:
		return c.Language(context.Background(), file)
	case meta:
		if *metaField != "" {
			return c.MetaField(context.Background(), file, *metaField)
		}
		if *recursive {
			mr, err := c.MetaRecursive(context.Background(), file)
			if err != nil {
				return "", err
			}
			bytes, err := json.MarshalIndent(mr, "", "  ")
			if err != nil {
				return "", err
			}
			return string(bytes), nil
		}
		return c.Meta(context.Background(), file)
	case version:
		return c.Version(context.Background())
	case parsers:
		p, err := c.Parsers(context.Background())
		if err != nil {
			return "", err
		}
		bytes, err := json.MarshalIndent(p, "", "  ")
		if err != nil {
			return "", err
		}
		return string(bytes), nil
	case mimeTypes:
		mt, err := c.MIMETypes(context.Background())
		if err != nil {
			return "", err
		}
		bytes, err := json.MarshalIndent(mt, "", "  ")
		if err != nil {
			return "", err
		}
		return string(bytes), nil
	case detectors:
		d, err := c.Detectors(context.Background())
		if err != nil {
			return "", err
		}
		bytes, err := json.MarshalIndent(d, "", "  ")
		if err != nil {
			return "", err
		}
		return string(bytes), nil
	}
}
<|endoftext|>


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

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/google/wuffs/lang/generate"
	"github.com/google/wuffs/lang/parse"

	cf "github.com/google/wuffs/cmd/commonflags"

	a "github.com/google/wuffs/lang/ast"
	t "github.com/google/wuffs/lang/token"
)

func doGen(wuffsRoot string, args []string) error    { return doGenGenlib(wuffsRoot, args, false) }
func doGenlib(wuffsRoot string, args []string) error { return doGenGenlib(wuffsRoot, args, true) }

func doGenGenlib(wuffsRoot string, args []string, genlib bool) error {
	flags := flag.NewFlagSet("gen", flag.ExitOnError)
	cformatterFlag := flags.String("cformatter", cf.CformatterDefault, cf.CformatterUsage)
	genlinenumFlag := flags.Bool("genlinenum", cf.GenlinenumDefault, cf.GenlinenumUsage)
	langsFlag := flags.String("langs", langsDefault, langsUsage)
	skipgendepsFlag := flags.Bool("skipgendeps", skipgendepsDefault, skipgendepsUsage)

	skipgenFlag := (*bool)(nil)
	if genlib {
		skipgenFlag = flags.Bool("skipgen", skipgenDefault, skipgenUsage)
	}
	versionFlag := (*string)(nil)
	if !genlib {
		versionFlag = flags.String("version", cf.VersionDefault, cf.VersionUsage)
	}

	if err := flags.Parse(args); err != nil {
		return err
	}
	if !cf.IsAlphaNumericIsh(*cformatterFlag) {
		return fmt.Errorf("bad -cformatter flag value %q", *cformatterFlag)
	}
	langs, err := parseLangs(*langsFlag)
	if err != nil {
		return err
	}
	v := cf.Version{}
	if !genlib {
		ok := false
		v, ok = cf.ParseVersion(*versionFlag)
		if !ok {
			return fmt.Errorf("bad -version flag value %q", *versionFlag)
		}
	}
	args = flags.Args()
	if len(args) == 0 {
		args = []string{"base", "std/..."}
	}

	h := genHelper{
		wuffsRoot:   wuffsRoot,
		langs:       langs,
		cformatter:  *cformatterFlag,
		genlinenum:  *genlinenumFlag,
		skipgen:     genlib && *skipgenFlag,
		skipgendeps: *skipgendepsFlag,
	}

	for _, arg := range args {
		recursive := strings.HasSuffix(arg, "/...")
		if recursive {
			arg = arg[:len(arg)-4]
		}
		if arg == "" {
			continue
		}

		if err := h.gen(arg, recursive); err != nil {
			return err
		}
	}

	if genlib {
		return h.genlibAffected()
	}
	return genrelease(wuffsRoot, langs, v)
}

type genHelper struct {
	wuffsRoot   string
	langs       []string
	cformatter  string
	genlinenum  bool
	skipgen     bool
	skipgendeps bool

	affected []string
	seen     map[string]struct{}
	tm       t.Map
}

func (h *genHelper) gen(dirname string, recursive bool) error {
	for len(dirname) > 0 && dirname[len(dirname)-1] == '/' {
		dirname = dirname[:len(dirname)-1]
	}

	if h.seen == nil {
		h.seen = map[string]struct{}{}
	} else if _, ok := h.seen[dirname]; ok {
		return nil
	}
	h.seen[dirname] = struct{}{}

	if dirname == "base" {
		if err := h.genDir(dirname, nil); err != nil {
			return err
		}
		h.affected = append(h.affected, dirname)
		return nil
	}

	if !cf.IsValidUsePath(dirname) {
		return fmt.Errorf("invalid package path %q", dirname)
	}

	qualFilenames, dirnames, err := listDir(
		filepath.Join(h.wuffsRoot, filepath.FromSlash(dirname)), ".wuffs", recursive)
	if err != nil {
		return err
	}
	if len(qualFilenames) > 0 {
		if err := h.genDir(dirname, qualFilenames); err != nil {
			return err
		}
		h.affected = append(h.affected, dirname)
	}
	if len(dirnames) > 0 {
		for _, d := range dirnames {
			if err := h.gen(dirname+"/"+d, recursive); err != nil {
				return err
			}
		}
	}
	return nil
}

func (h *genHelper) genDir(dirname string, qualFilenames []string) error {
	// TODO: skip the generation if the output file already exists and its
	// mtime is newer than all inputs and the wuffs-gen-foo command.

	packageName := path.Base(dirname)
	if !validName(packageName) {
		return fmt.Errorf(`invalid package %q, not in [a-z0-9]+`, packageName)
	}

	if h.skipgen {
		return nil
	}
	if !h.skipgendeps {
		if err := h.genDirDependencies(qualFilenames); err != nil {
			return err
		}
	}

	for _, lang := range h.langs {
		command := "wuffs-" + lang
		cmdArgs := []string{"gen", "-package_name", packageName}
		if (lang == "c") && (h.cformatter != cf.CformatterDefault) {
			cmdArgs = append(cmdArgs, fmt.Sprintf("-cformatter=%s", h.cformatter))
		}
		if h.genlinenum != cf.GenlinenumDefault {
			cmdArgs = append(cmdArgs, fmt.Sprintf("-genlinenum=%t", h.genlinenum))
		}
		cmdArgs = append(cmdArgs, qualFilenames...)
		stdout := &bytes.Buffer{}

		cmd := exec.Command(command, cmdArgs...)
		cmd.Stdin = nil
		cmd.Stdout = stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err == nil {
			// No-op.
		} else if _, ok := err.(*exec.ExitError); ok {
			return fmt.Errorf("%s: failed", command)
		} else {
			return err
		}
		out := stdout.Bytes()

		flatDirname := fmt.Sprintf("wuffs-%s", strings.Replace(dirname, "/", "-", -1))
		if err := h.genFile(flatDirname, lang, out); err != nil {
			return err
		}
	}
	if len(h.langs) > 0 && packageName != "base" {
		if err := h.genWuffs(dirname, qualFilenames); err != nil {
			return err
		}
	}
	return nil
}

func (h *genHelper) genDirDependencies(qualifiedFilenames []string) error {
	files, err := generate.ParseFiles(&h.tm, qualifiedFilenames, nil)
	if err != nil {
		return err
	}
	for _, f := range files {
		for _, n := range f.TopLevelDecls() {
			if n.Kind() != a.KUse {
				continue
			}
			useDirname := h.tm.ByID(n.AsUse().Path())
			useDirname, _ = t.Unescape(useDirname)
			if err := h.gen(useDirname, false); err != nil {
				return err
			}
		}
	}
	return h.gen("base", false)
}

func (h *genHelper) genFile(dirname string, lang string, out []byte) error {
	return writeFile(
		filepath.Join(h.wuffsRoot, "gen", lang, filepath.FromSlash(dirname)+"."+lang),
		out,
	)
}

func (h *genHelper) genWuffs(dirname string, qualifiedFilenames []string) error {
	files, err := generate.ParseFiles(&h.tm, qualifiedFilenames, &parse.Options{
		AllowDoubleUnderscoreNames: true,
	})
	if err != nil {
		return err
	}

	out := &bytes.Buffer{}
	fmt.Fprintf(out, "// Code generated by running \"wuffs gen\". DO NOT EDIT.\n\n")

	for _, f := range files {
		for _, n := range f.TopLevelDecls() {
			switch n.Kind() {
			case a.KConst:
				n := n.AsConst()
				if !n.Public() {
					continue
				}
				fmt.Fprintf(out, "pub const %s %s = %v\n",
					n.QID().Str(&h.tm), n.XType().Str(&h.tm), n.Value().Str(&h.tm))

			case a.KFunc:
				n := n.AsFunc()
				if !n.Public() {
					continue
				}
				if n.Receiver().IsZero() {
					return fmt.Errorf("TODO: genWuffs for a free-standing function")
				}
				// TODO: look at n.Asserts().
				fmt.Fprintf(out, "pub func %s.%s%v(", n.Receiver().Str(&h.tm), n.FuncName().Str(&h.tm), n.Effect())
				for i, field := range n.In().Fields() {
					field := field.AsField()
					if i > 0 {
						fmt.Fprintf(out, ", ")
					}
					// TODO: what happens if the XType is from another package?
					// Similarly for the out-param.
					fmt.Fprintf(out, "%s: %s", field.Name().Str(&h.tm), field.XType().Str(&h.tm))
				}
				fmt.Fprintf(out, ") ")
				if o := n.Out(); o != nil {
					fmt.Fprintf(out, "%s", o.Str(&h.tm))
				}
				fmt.Fprintf(out, " { }\n")

			case a.KStatus:
				n := n.AsStatus()
				if !n.Public() {
					continue
				}
				fmt.Fprintf(out, "pub status %s\n", n.QID().Str(&h.tm))

			case a.KStruct:
				n := n.AsStruct()
				if !n.Public() {
					continue
				}
				classy := ""
				if n.Classy() {
					classy = "?"
				}
				fmt.Fprintf(out, "pub struct %s%s()\n", n.QID().Str(&h.tm), classy)
			}
		}
	}
	return h.genFile(dirname, "wuffs", out.Bytes())
}

func (h *genHelper) genlibAffected() error {
	for _, lang := range h.langs {
		command := "wuffs-" + lang
		args := []string{"genlib"}
		args = append(args, "-dstdir", filepath.Join(h.wuffsRoot, "gen", "lib", lang))
		args = append(args, "-srcdir", filepath.Join(h.wuffsRoot, "gen", lang))
		args = append(args, h.affected...)
		cmd := exec.Command(command, args...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Have "wuffs genlib" take a -ccompilers arg<commit_after>// Copyright 2017 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/google/wuffs/lang/generate"
	"github.com/google/wuffs/lang/parse"

	cf "github.com/google/wuffs/cmd/commonflags"

	a "github.com/google/wuffs/lang/ast"
	t "github.com/google/wuffs/lang/token"
)

func doGen(wuffsRoot string, args []string) error    { return doGenGenlib(wuffsRoot, args, false) }
func doGenlib(wuffsRoot string, args []string) error { return doGenGenlib(wuffsRoot, args, true) }

func doGenGenlib(wuffsRoot string, args []string, genlib bool) error {
	flags := flag.NewFlagSet("gen", flag.ExitOnError)
	cformatterFlag := flags.String("cformatter", cf.CformatterDefault, cf.CformatterUsage)
	genlinenumFlag := flags.Bool("genlinenum", cf.GenlinenumDefault, cf.GenlinenumUsage)
	langsFlag := flags.String("langs", langsDefault, langsUsage)
	skipgendepsFlag := flags.Bool("skipgendeps", skipgendepsDefault, skipgendepsUsage)

	ccompilersFlag := (*string)(nil)
	skipgenFlag := (*bool)(nil)
	versionFlag := (*string)(nil)
	if genlib {
		ccompilersFlag = flags.String("ccompilers", cf.CcompilersDefault, cf.CcompilersUsage)
		skipgenFlag = flags.Bool("skipgen", skipgenDefault, skipgenUsage)
	} else {
		versionFlag = flags.String("version", cf.VersionDefault, cf.VersionUsage)
	}

	if err := flags.Parse(args); err != nil {
		return err
	}
	if genlib {
		if !cf.IsAlphaNumericIsh(*ccompilersFlag) {
			return fmt.Errorf("bad -ccompilers flag value %q", *ccompilersFlag)
		}
	}
	if !cf.IsAlphaNumericIsh(*cformatterFlag) {
		return fmt.Errorf("bad -cformatter flag value %q", *cformatterFlag)
	}
	langs, err := parseLangs(*langsFlag)
	if err != nil {
		return err
	}
	v := cf.Version{}
	if !genlib {
		ok := false
		v, ok = cf.ParseVersion(*versionFlag)
		if !ok {
			return fmt.Errorf("bad -version flag value %q", *versionFlag)
		}
	}
	args = flags.Args()
	if len(args) == 0 {
		args = []string{"base", "std/..."}
	}

	h := genHelper{
		wuffsRoot:   wuffsRoot,
		langs:       langs,
		ccompilers:  *ccompilersFlag,
		cformatter:  *cformatterFlag,
		genlinenum:  *genlinenumFlag,
		skipgen:     genlib && *skipgenFlag,
		skipgendeps: *skipgendepsFlag,
	}

	for _, arg := range args {
		recursive := strings.HasSuffix(arg, "/...")
		if recursive {
			arg = arg[:len(arg)-4]
		}
		if arg == "" {
			continue
		}

		if err := h.gen(arg, recursive); err != nil {
			return err
		}
	}

	if genlib {
		return h.genlibAffected()
	}
	return genrelease(wuffsRoot, langs, v)
}

type genHelper struct {
	wuffsRoot   string
	langs       []string
	ccompilers  string
	cformatter  string
	genlinenum  bool
	skipgen     bool
	skipgendeps bool

	affected []string
	seen     map[string]struct{}
	tm       t.Map
}

func (h *genHelper) gen(dirname string, recursive bool) error {
	for len(dirname) > 0 && dirname[len(dirname)-1] == '/' {
		dirname = dirname[:len(dirname)-1]
	}

	if h.seen == nil {
		h.seen = map[string]struct{}{}
	} else if _, ok := h.seen[dirname]; ok {
		return nil
	}
	h.seen[dirname] = struct{}{}

	if dirname == "base" {
		if err := h.genDir(dirname, nil); err != nil {
			return err
		}
		h.affected = append(h.affected, dirname)
		return nil
	}

	if !cf.IsValidUsePath(dirname) {
		return fmt.Errorf("invalid package path %q", dirname)
	}

	qualFilenames, dirnames, err := listDir(
		filepath.Join(h.wuffsRoot, filepath.FromSlash(dirname)), ".wuffs", recursive)
	if err != nil {
		return err
	}
	if len(qualFilenames) > 0 {
		if err := h.genDir(dirname, qualFilenames); err != nil {
			return err
		}
		h.affected = append(h.affected, dirname)
	}
	if len(dirnames) > 0 {
		for _, d := range dirnames {
			if err := h.gen(dirname+"/"+d, recursive); err != nil {
				return err
			}
		}
	}
	return nil
}

func (h *genHelper) genDir(dirname string, qualFilenames []string) error {
	// TODO: skip the generation if the output file already exists and its
	// mtime is newer than all inputs and the wuffs-gen-foo command.

	packageName := path.Base(dirname)
	if !validName(packageName) {
		return fmt.Errorf(`invalid package %q, not in [a-z0-9]+`, packageName)
	}

	if h.skipgen {
		return nil
	}
	if !h.skipgendeps {
		if err := h.genDirDependencies(qualFilenames); err != nil {
			return err
		}
	}

	for _, lang := range h.langs {
		command := "wuffs-" + lang
		cmdArgs := []string{"gen", "-package_name", packageName}
		if (lang == "c") && (h.cformatter != cf.CformatterDefault) {
			cmdArgs = append(cmdArgs, fmt.Sprintf("-cformatter=%s", h.cformatter))
		}
		if h.genlinenum != cf.GenlinenumDefault {
			cmdArgs = append(cmdArgs, fmt.Sprintf("-genlinenum=%t", h.genlinenum))
		}
		cmdArgs = append(cmdArgs, qualFilenames...)
		stdout := &bytes.Buffer{}

		cmd := exec.Command(command, cmdArgs...)
		cmd.Stdin = nil
		cmd.Stdout = stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err == nil {
			// No-op.
		} else if _, ok := err.(*exec.ExitError); ok {
			return fmt.Errorf("%s: failed", command)
		} else {
			return err
		}
		out := stdout.Bytes()

		flatDirname := fmt.Sprintf("wuffs-%s", strings.Replace(dirname, "/", "-", -1))
		if err := h.genFile(flatDirname, lang, out); err != nil {
			return err
		}
	}
	if len(h.langs) > 0 && packageName != "base" {
		if err := h.genWuffs(dirname, qualFilenames); err != nil {
			return err
		}
	}
	return nil
}

func (h *genHelper) genDirDependencies(qualifiedFilenames []string) error {
	files, err := generate.ParseFiles(&h.tm, qualifiedFilenames, nil)
	if err != nil {
		return err
	}
	for _, f := range files {
		for _, n := range f.TopLevelDecls() {
			if n.Kind() != a.KUse {
				continue
			}
			useDirname := h.tm.ByID(n.AsUse().Path())
			useDirname, _ = t.Unescape(useDirname)
			if err := h.gen(useDirname, false); err != nil {
				return err
			}
		}
	}
	return h.gen("base", false)
}

func (h *genHelper) genFile(dirname string, lang string, out []byte) error {
	return writeFile(
		filepath.Join(h.wuffsRoot, "gen", lang, filepath.FromSlash(dirname)+"."+lang),
		out,
	)
}

func (h *genHelper) genWuffs(dirname string, qualifiedFilenames []string) error {
	files, err := generate.ParseFiles(&h.tm, qualifiedFilenames, &parse.Options{
		AllowDoubleUnderscoreNames: true,
	})
	if err != nil {
		return err
	}

	out := &bytes.Buffer{}
	fmt.Fprintf(out, "// Code generated by running \"wuffs gen\". DO NOT EDIT.\n\n")

	for _, f := range files {
		for _, n := range f.TopLevelDecls() {
			switch n.Kind() {
			case a.KConst:
				n := n.AsConst()
				if !n.Public() {
					continue
				}
				fmt.Fprintf(out, "pub const %s %s = %v\n",
					n.QID().Str(&h.tm), n.XType().Str(&h.tm), n.Value().Str(&h.tm))

			case a.KFunc:
				n := n.AsFunc()
				if !n.Public() {
					continue
				}
				if n.Receiver().IsZero() {
					return fmt.Errorf("TODO: genWuffs for a free-standing function")
				}
				// TODO: look at n.Asserts().
				fmt.Fprintf(out, "pub func %s.%s%v(", n.Receiver().Str(&h.tm), n.FuncName().Str(&h.tm), n.Effect())
				for i, field := range n.In().Fields() {
					field := field.AsField()
					if i > 0 {
						fmt.Fprintf(out, ", ")
					}
					// TODO: what happens if the XType is from another package?
					// Similarly for the out-param.
					fmt.Fprintf(out, "%s: %s", field.Name().Str(&h.tm), field.XType().Str(&h.tm))
				}
				fmt.Fprintf(out, ") ")
				if o := n.Out(); o != nil {
					fmt.Fprintf(out, "%s", o.Str(&h.tm))
				}
				fmt.Fprintf(out, " { }\n")

			case a.KStatus:
				n := n.AsStatus()
				if !n.Public() {
					continue
				}
				fmt.Fprintf(out, "pub status %s\n", n.QID().Str(&h.tm))

			case a.KStruct:
				n := n.AsStruct()
				if !n.Public() {
					continue
				}
				classy := ""
				if n.Classy() {
					classy = "?"
				}
				fmt.Fprintf(out, "pub struct %s%s()\n", n.QID().Str(&h.tm), classy)
			}
		}
	}
	return h.genFile(dirname, "wuffs", out.Bytes())
}

func (h *genHelper) genlibAffected() error {
	for _, lang := range h.langs {
		command := "wuffs-" + lang
		args := []string{"genlib"}
		args = append(args, "-dstdir", filepath.Join(h.wuffsRoot, "gen", "lib", lang))
		args = append(args, "-srcdir", filepath.Join(h.wuffsRoot, "gen", lang))
		if lang == "c" {
			args = append(args, fmt.Sprintf("-ccompilers=%s", h.ccompilers))
		}
		args = append(args, h.affected...)
		cmd := exec.Command(command, args...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"go-bots/ev3"
	"os"
	"time"
)

var devs *ev3.Devices
var initializationTime time.Time
var motorL1, motorL2, motorR1, motorR2 *ev3.Attribute
var irL, irFL, irFR, irR *ev3.Attribute

func initialize() {
	initializationTime = time.Now()

	devs = ev3.Scan(&ev3.OutPortModes{
		OutA: ev3.OutPortModeDcMotor,
		OutB: ev3.OutPortModeDcMotor,
		OutC: ev3.OutPortModeDcMotor,
		OutD: ev3.OutPortModeDcMotor,
	})

	// Check motors
	ev3.CheckDriver(devs.OutA, ev3.DriverRcxMotor, ev3.OutA)
	ev3.CheckDriver(devs.OutB, ev3.DriverRcxMotor, ev3.OutB)
	ev3.CheckDriver(devs.OutC, ev3.DriverRcxMotor, ev3.OutC)
	ev3.CheckDriver(devs.OutD, ev3.DriverRcxMotor, ev3.OutD)

	// Check sensors
	ev3.CheckDriver(devs.In1, ev3.DriverIr, ev3.In1)
	ev3.CheckDriver(devs.In2, ev3.DriverIr, ev3.In2)
	ev3.CheckDriver(devs.In3, ev3.DriverIr, ev3.In3)
	ev3.CheckDriver(devs.In4, ev3.DriverIr, ev3.In4)

	// Set sensors mode
	ev3.SetMode(devs.In1, ev3.IrModeProx)
	ev3.SetMode(devs.In2, ev3.IrModeProx)
	ev3.SetMode(devs.In3, ev3.IrModeProx)
	ev3.SetMode(devs.In4, ev3.IrModeProx)

	// Stop motors
	ev3.RunCommand(devs.OutA, ev3.CmdStop)
	ev3.RunCommand(devs.OutB, ev3.CmdStop)
	ev3.RunCommand(devs.OutC, ev3.CmdStop)
	ev3.RunCommand(devs.OutD, ev3.CmdStop)

	// Put motors in direct mode
	ev3.RunCommand(devs.OutA, ev3.CmdRunDirect)
	ev3.RunCommand(devs.OutB, ev3.CmdRunDirect)
	ev3.RunCommand(devs.OutC, ev3.CmdRunDirect)
	ev3.RunCommand(devs.OutD, ev3.CmdRunDirect)

	// Open motors
	motorL1 = ev3.OpenTextW(devs.OutA, ev3.DutyCycleSp)
	motorL2 = ev3.OpenTextW(devs.OutB, ev3.DutyCycleSp)
	motorR1 = ev3.OpenTextW(devs.OutC, ev3.DutyCycleSp)
	motorR2 = ev3.OpenTextW(devs.OutD, ev3.DutyCycleSp)

	// Open sensors
	irL = ev3.OpenByteR(devs.In1, ev3.BinData)
	irFL = ev3.OpenByteR(devs.In2, ev3.BinData)
	irFR = ev3.OpenByteR(devs.In3, ev3.BinData)
	irR = ev3.OpenByteR(devs.In4, ev3.BinData)
}

func close() {
	// Stop motors
	ev3.RunCommand(devs.OutA, ev3.CmdStop)
	ev3.RunCommand(devs.OutB, ev3.CmdStop)
	ev3.RunCommand(devs.OutC, ev3.CmdStop)
	ev3.RunCommand(devs.OutD, ev3.CmdStop)

	// Close motors
	motorL1.Close()
	motorL2.Close()
	motorR1.Close()
	motorR2.Close()

	// Close sensors
	irL.Close()
	irFL.Close()
	irFR.Close()
	irR.Close()
}

var lastMoveTicks int
var lastSpeedLeft int
var lastSpeedRight int

const accelPerTicks int = 1

func move(left int, right int, now int) {
	ticks := now - lastMoveTicks
	lastMoveTicks = now

	nextSpeedLeft := lastSpeedLeft
	nextSpeedRight := lastSpeedRight

	if left > nextSpeedLeft {
		nextSpeedLeft += (ticks * accelPerTicks)
		if nextSpeedLeft > left {
			nextSpeedLeft = left
		}
	} else if left < nextSpeedLeft {
		nextSpeedLeft -= (ticks * accelPerTicks)
		if nextSpeedLeft < left {
			nextSpeedLeft = left
		}
	}
	if right > nextSpeedRight {
		nextSpeedRight += (ticks * accelPerTicks)
		if nextSpeedRight > right {
			nextSpeedRight = right
		}
	} else if right < nextSpeedRight {
		nextSpeedRight -= (ticks * accelPerTicks)
		if nextSpeedRight < right {
			nextSpeedRight = right
		}
	}
	lastSpeedLeft = nextSpeedLeft
	lastSpeedRight = nextSpeedRight

	motorL1.Value = -nextSpeedLeft / 10000
	motorL2.Value = nextSpeedLeft / 10000
	motorR1.Value = -nextSpeedRight / 10000
	motorR2.Value = -nextSpeedRight / 10000

	motorL1.Sync()
	motorL2.Sync()
	motorR1.Sync()
	motorR2.Sync()
}

func read() {
	irL.Sync()
	irFL.Sync()
	irFR.Sync()
	irR.Sync()
}

func durationToTicks(d time.Duration) int {
	return int(d / 1000)
}
func timespanAsTicks(start time.Time, end time.Time) int {
	return durationToTicks(end.Sub(start))
}
func currentTicks() int {
	return timespanAsTicks(initializationTime, time.Now())
}
func ticksToMillis(ticks int) int {
	return ticks / 1000
}

func print(data ...interface{}) {
	fmt.Fprintln(os.Stderr, data)
}

func main() {
	initialize()
	defer close()

	start := currentTicks()
	for {
		now := currentTicks()
		if ticksToMillis(now-start) > 100 {
			break
		}
		read()
		move(0, 0, now)
	}
	start = currentTicks()
	for {
		now := currentTicks()
		if ticksToMillis(now-start) > 1000 {
			break
		}
		read()
		move(1000000, 1000000, now)
	}
}
<commit_msg>Fixed speed ramp.<commit_after>package main

import (
	"fmt"
	"go-bots/ev3"
	"os"
	"time"
)

var devs *ev3.Devices
var initializationTime time.Time
var motorL1, motorL2, motorR1, motorR2 *ev3.Attribute
var irL, irFL, irFR, irR *ev3.Attribute

func initialize() {
	initializationTime = time.Now()

	devs = ev3.Scan(&ev3.OutPortModes{
		OutA: ev3.OutPortModeDcMotor,
		OutB: ev3.OutPortModeDcMotor,
		OutC: ev3.OutPortModeDcMotor,
		OutD: ev3.OutPortModeDcMotor,
	})

	// Check motors
	ev3.CheckDriver(devs.OutA, ev3.DriverRcxMotor, ev3.OutA)
	ev3.CheckDriver(devs.OutB, ev3.DriverRcxMotor, ev3.OutB)
	ev3.CheckDriver(devs.OutC, ev3.DriverRcxMotor, ev3.OutC)
	ev3.CheckDriver(devs.OutD, ev3.DriverRcxMotor, ev3.OutD)

	// Check sensors
	ev3.CheckDriver(devs.In1, ev3.DriverIr, ev3.In1)
	ev3.CheckDriver(devs.In2, ev3.DriverIr, ev3.In2)
	ev3.CheckDriver(devs.In3, ev3.DriverIr, ev3.In3)
	ev3.CheckDriver(devs.In4, ev3.DriverIr, ev3.In4)

	// Set sensors mode
	ev3.SetMode(devs.In1, ev3.IrModeProx)
	ev3.SetMode(devs.In2, ev3.IrModeProx)
	ev3.SetMode(devs.In3, ev3.IrModeProx)
	ev3.SetMode(devs.In4, ev3.IrModeProx)

	// Stop motors
	ev3.RunCommand(devs.OutA, ev3.CmdStop)
	ev3.RunCommand(devs.OutB, ev3.CmdStop)
	ev3.RunCommand(devs.OutC, ev3.CmdStop)
	ev3.RunCommand(devs.OutD, ev3.CmdStop)

	// Open motors
	motorL1 = ev3.OpenTextW(devs.OutA, ev3.DutyCycleSp)
	motorL2 = ev3.OpenTextW(devs.OutB, ev3.DutyCycleSp)
	motorR1 = ev3.OpenTextW(devs.OutC, ev3.DutyCycleSp)
	motorR2 = ev3.OpenTextW(devs.OutD, ev3.DutyCycleSp)

	// Reset motor speed
	motorL1.Value = 0
	motorL2.Value = 0
	motorR1.Value = 0
	motorR2.Value = 0

	motorL1.Sync()
	motorL2.Sync()
	motorR1.Sync()
	motorR2.Sync()

	// Put motors in direct mode
	ev3.RunCommand(devs.OutA, ev3.CmdRunDirect)
	ev3.RunCommand(devs.OutB, ev3.CmdRunDirect)
	ev3.RunCommand(devs.OutC, ev3.CmdRunDirect)
	ev3.RunCommand(devs.OutD, ev3.CmdRunDirect)

	// Open sensors
	irL = ev3.OpenByteR(devs.In1, ev3.BinData)
	irFL = ev3.OpenByteR(devs.In2, ev3.BinData)
	irFR = ev3.OpenByteR(devs.In3, ev3.BinData)
	irR = ev3.OpenByteR(devs.In4, ev3.BinData)
}

func close() {
	// Stop motors
	ev3.RunCommand(devs.OutA, ev3.CmdStop)
	ev3.RunCommand(devs.OutB, ev3.CmdStop)
	ev3.RunCommand(devs.OutC, ev3.CmdStop)
	ev3.RunCommand(devs.OutD, ev3.CmdStop)

	// Close motors
	motorL1.Close()
	motorL2.Close()
	motorR1.Close()
	motorR2.Close()

	// Close sensors
	irL.Close()
	irFL.Close()
	irFR.Close()
	irR.Close()
}

var lastMoveTicks int
var lastSpeedLeft int
var lastSpeedRight int

const accelPerTicks int = 5

func move(left int, right int, now int) {
	ticks := now - lastMoveTicks
	lastMoveTicks = now

	nextSpeedLeft := lastSpeedLeft
	nextSpeedRight := lastSpeedRight
	delta := ticks * accelPerTicks
	// delta := ticks * ticks * accelPerTicks

	if left > nextSpeedLeft {
		nextSpeedLeft += delta
		if nextSpeedLeft > left {
			nextSpeedLeft = left
		}
	} else if left < nextSpeedLeft {
		nextSpeedLeft -= delta
		if nextSpeedLeft < left {
			nextSpeedLeft = left
		}
	}
	if right > nextSpeedRight {
		nextSpeedRight += delta
		if nextSpeedRight > right {
			nextSpeedRight = right
		}
	} else if right < nextSpeedRight {
		nextSpeedRight -= delta
		if nextSpeedRight < right {
			nextSpeedRight = right
		}
	}
	lastSpeedLeft = nextSpeedLeft
	lastSpeedRight = nextSpeedRight

	motorL1.Value = -nextSpeedLeft / 10000
	motorL2.Value = nextSpeedLeft / 10000
	motorR1.Value = -nextSpeedRight / 10000
	motorR2.Value = -nextSpeedRight / 10000

	motorL1.Sync()
	motorL2.Sync()
	motorR1.Sync()
	motorR2.Sync()
}

func read() {
	irL.Sync()
	irFL.Sync()
	irFR.Sync()
	irR.Sync()
}

func durationToTicks(d time.Duration) int {
	return int(d / 1000)
}
func timespanAsTicks(start time.Time, end time.Time) int {
	return durationToTicks(end.Sub(start))
}
func currentTicks() int {
	return timespanAsTicks(initializationTime, time.Now())
}
func ticksToMillis(ticks int) int {
	return ticks / 1000
}

func print(data ...interface{}) {
	fmt.Fprintln(os.Stderr, data)
}

func main() {
	initialize()
	defer close()

	start := currentTicks()
	for {
		now := currentTicks()
		if ticksToMillis(now-start) > 100 {
			break
		}
		read()
		move(0, 0, now)
	}
	start = currentTicks()
	for {
		now := currentTicks()
		if ticksToMillis(now-start) > 1000 {
			break
		}
		read()
		move(1000000, 1000000, now)
	}
}
<|endoftext|>


<commit_before>package database

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"fmt"
	"io"

	"github.com/asdine/storm/v3/codec"
)

const name = "aes-"

// AES is an Codec that encrypts the data and uses a sub marshaller to actually serialize the data
type AES struct {
	subMarshaller codec.MarshalUnmarshaler
	aesGCM        cipher.AEAD
}

// NewAES creates a new AES encryption marshaller. It takes a sub marshaller to actually serialize the data and a 16/32 bytes private key to
// encrypt all data using AES in GCM block mode.
func NewAES(subMarshaller codec.MarshalUnmarshaler, key []byte) (*AES, error) {
	aesCipher, err := aes.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("error creating AES cipher: %w", err)
	}

	aesGCM, err := cipher.NewGCM(aesCipher)
	if err != nil {
		return nil, fmt.Errorf("error creating GCM block mode: %w", err)
	}

	return &AES{
		subMarshaller: subMarshaller,
		aesGCM:        aesGCM,
	}, nil
}

func (c *AES) Name() string {
	// Return a dynamic name, because the marshalling will also fail if the inner marshalling changes.
	return name + c.subMarshaller.Name()
}

func (c *AES) Marshal(v interface{}) ([]byte, error) {
	data, err := c.subMarshaller.Marshal(v)
	if err != nil {
		return nil, err
	}

	nonce := make([]byte, c.aesGCM.NonceSize())
	_, err = io.ReadFull(rand.Reader, nonce)
	if err != nil {
		return nil, fmt.Errorf("error reading random nonce: %w", err)
	}

	return c.aesGCM.Seal(nonce, nonce, data, nil), nil
}

func (c *AES) Unmarshal(data []byte, v interface{}) error {
	nonceSize := c.aesGCM.NonceSize()
	decrypted, err := c.aesGCM.Open(nil, data[:nonceSize], data[nonceSize:], nil)
	if err != nil {
		return fmt.Errorf("error decrypting data: %w", err)
	}

	return c.subMarshaller.Unmarshal(decrypted, v)
}
<commit_msg>Add explanatory comments<commit_after>package database

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"fmt"
	"io"

	"github.com/asdine/storm/v3/codec"
)

const name = "aes-"

// AES is an Codec that encrypts the data and uses a sub marshaller to actually serialize the data
type AES struct {
	subMarshaller codec.MarshalUnmarshaler
	aesGCM        cipher.AEAD
}

// NewAES creates a new AES encryption marshaller. It takes a sub marshaller to actually serialize the data and a 16/32 bytes private key to
// encrypt all data using AES in GCM block mode.
func NewAES(subMarshaller codec.MarshalUnmarshaler, key []byte) (*AES, error) {
	aesCipher, err := aes.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("error creating AES cipher: %w", err)
	}

	aesGCM, err := cipher.NewGCM(aesCipher)
	if err != nil {
		return nil, fmt.Errorf("error creating GCM block mode: %w", err)
	}

	return &AES{
		subMarshaller: subMarshaller,
		aesGCM:        aesGCM,
	}, nil
}

// Name returns a compound of the inner marshaller prefixed by 'aes-'
func (c *AES) Name() string {
	// Return a dynamic name, because the marshalling will also fail if the inner marshalling changes.
	return name + c.subMarshaller.Name()
}

// Marshal marshals the given data object to an encrypted byte array
func (c *AES) Marshal(v interface{}) ([]byte, error) {
	data, err := c.subMarshaller.Marshal(v)
	if err != nil {
		return nil, err
	}

	nonce := make([]byte, c.aesGCM.NonceSize())
	_, err = io.ReadFull(rand.Reader, nonce)
	if err != nil {
		return nil, fmt.Errorf("error reading random nonce: %w", err)
	}

	return c.aesGCM.Seal(nonce, nonce, data, nil), nil
}

// Unmarshal unmarshals the given encrypted byte array to the given type
func (c *AES) Unmarshal(data []byte, v interface{}) error {
	nonceSize := c.aesGCM.NonceSize()
	decrypted, err := c.aesGCM.Open(nil, data[:nonceSize], data[nonceSize:], nil)
	if err != nil {
		return fmt.Errorf("error decrypting data: %w", err)
	}

	return c.subMarshaller.Unmarshal(decrypted, v)
}
<|endoftext|>


<commit_before>package core

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"sync"
	"time"

	"github.com/tinzenite/channel"
	"github.com/tinzenite/model"
	"github.com/tinzenite/shared"
)

/*
Tinzenite is the struct on which all important operations should be called.
*/
type Tinzenite struct {
	Path           string
	auth           *Authentication
	selfpeer       *shared.Peer
	channel        *channel.Channel
	cInterface     *chaninterface
	allPeers       []*shared.Peer
	model          *model.Model
	sendChannel    chan shared.UpdateMessage
	muteFlag       bool
	stop           chan bool
	wg             sync.WaitGroup
	peerValidation PeerValidation
}

/*
SyncRemote updates first locally and then sync remotely if other peers are
connected. NOTE: Both sync methods can block for a potentially long time,
especially when first run!
*/
func (t *Tinzenite) SyncRemote() error {
	// mute updates because we'll sync models later
	t.muteFlag = true
	// defer setting it back guaranteed
	defer func() { t.muteFlag = false }()
	// first ensure that local model is up to date
	err := t.SyncLocal()
	if err != nil {
		return err
	}
	online, err := t.channel.OnlineAddresses()
	if err != nil {
		return err
	}
	for _, address := range online {
		t.cInterface.SyncModel(address)
	}
	return nil
}

/*
SyncLocal changes. Will send updates to connected peers but not synchronize with
other peers.
*/
func (t *Tinzenite) SyncLocal() error {
	// TODO this shouldn't be here... place somewhere else?
	// update peers
	err := t.applyPeers()
	if err != nil {
		return err
	}
	// update model
	return t.model.Update()
}

/*
Address of this Tinzenite peer that can be used to connect to.
*/
func (t *Tinzenite) Address() (string, error) {
	return t.channel.ConnectionAddress()
}

/*
Name of this Tinzenite peer.
*/
func (t *Tinzenite) Name() string {
	return t.selfpeer.Name
}

/*
Close cleanly stores everything and shuts Tinzenite down.
*/
func (t *Tinzenite) Close() {
	// send stop signal
	t.stop <- false
	// wait for it to close
	t.wg.Wait()
	// store all information
	t.Store()
	// FINALLY close (afterwards because I still need info from channel for store!)
	t.channel.Close()
}

/*
Store the tinzenite directory structure to disk. Will resolve all important
objects and store them so that it can later be reloaded. NOTE: Will not update
the full model, so be sure to have called Update() to guarantee an up to date
save.
*/
func (t *Tinzenite) Store() error {
	err := shared.MakeDotTinzenite(t.Path)
	if err != nil {
		return err
	}
	// write all peers to files
	for _, peer := range t.allPeers {
		err := peer.StoreTo(t.Path + "/" + shared.STOREPEERDIR)
		if err != nil {
			return err
		}
	}
	// store local peer info with toxdata
	toxData, err := t.channel.ToxData()
	if err != nil {
		return err
	}
	toxPeerDump := &shared.ToxPeerDump{
		SelfPeer: t.selfpeer,
		ToxData:  toxData}
	err = toxPeerDump.StoreTo(t.Path + "/" + shared.STORETOXDUMPDIR)
	if err != nil {
		return err
	}
	// store auth file
	err = t.auth.StoreTo(t.Path + "/" + shared.STOREAUTHDIR)
	if err != nil {
		return err
	}
	// update model for tinzenite dir to catch above stores
	err = t.model.PartialUpdate(t.Path + "/" + shared.TINZENITEDIR)
	if err != nil {
		return err
	}
	// finally store model (last because peers and org stuff is included!)
	return t.model.Store()
}

/*
PrintStatus returns a formatted string of the peer status.
*/
func (t *Tinzenite) PrintStatus() string {
	var out string
	out += "Online:\n"
	addresses, err := t.channel.FriendAddresses()
	if err != nil {
		out += "channel.FriendAddresses failed!"
	} else {
		var count int
		for _, address := range addresses {
			online, err := t.channel.IsAddressOnline(address)
			var insert string
			if err != nil {
				insert = "ERROR"
			} else {
				insert = fmt.Sprintf("%v", online)
			}
			out += address[:16] + " :: " + insert + "\n"
			count++
		}
		out += "Total friends: " + fmt.Sprintf("%d", count)
	}
	return out
}

/*
DisconnectPeer does exactly that. NOTE: this is a passive action and doesn't do
anything except remove the peer from the network. The peer is not further
notified.

TODO: maybe not use name but Identification?
TODO: when will other peers remove it? They need to remove the contact info from the channel... FIXME
*/
func (t *Tinzenite) DisconnectPeer(peerName string) {
	var newPeers []*shared.Peer
	for _, peer := range t.allPeers {
		if t.selfpeer.Identification == peer.Identification {
			continue
		}
		if peer.Name == peerName {
			log.Println("Removing", peer.Name, "at", peer.Address[:8])
			// delete peer file
			path := shared.CreatePath(t.Path, shared.TINZENITEDIR+"/"+shared.ORGDIR+"/"+shared.PEERSDIR+"/"+peer.Identification+shared.ENDING)
			err := t.model.ApplyRemove(path, nil)
			if err != nil {
				log.Println("DisconnectPeer:", err)
			}
			// remove from channel
			err = t.channel.RemoveConnection(peer.Address)
			if err != nil {
				log.Println("DisconnectPeer:", err)
			}
			// write peer to all removals so that no removals will be orphaned
			removePath := t.Path + "/" + shared.TINZENITEDIR + "/" + shared.REMOVEDIR
			allRemovals, _ := ioutil.ReadDir(removePath)
			// for every object that is currently being removed
			for _, stat := range allRemovals {
				// write the to be removed peer as done
				err := t.model.UpdateRemovalDir(stat.Name(), peer.Identification)
				if err != nil {
					// warn if it failed
					log.Println("Tinzenite: failed to purge removed peer from removal!")
				}
			}
			// continue does not readd to tinzenite, removing the reference to it
			continue
		}
		newPeers = append(newPeers, peer)
	}
	t.allPeers = newPeers
}

/*
applyPeers loads all peers and readies the communication channel accordingly.
*/
func (t *Tinzenite) applyPeers() error {
	peers, err := shared.LoadPeers(t.Path)
	if err != nil {
		return err
	}
	// make sure they are all tox ready
	for _, peer := range peers {
		// tox will return an error if the address has already been added, so we just ignore it
		_ = t.channel.AcceptConnection(peer.Address)
	}
	// finally apply
	t.allPeers = peers
	return nil
}

/*
Send the given message to the peer with the address if online.
*/
func (t *Tinzenite) send(address, msg string) error {
	return t.channel.Send(address, msg)
}

/*
Merge an update message to the local model.
*/
func (t *Tinzenite) merge(msg *shared.UpdateMessage) error {
	relPath := shared.CreatePath(t.Path, msg.Object.Path)
	// first: apply local changes to model (this is why writing PartialUpdate was no waste of time, isn't this cool?! :D)
	err := t.model.PartialUpdate(relPath.FullPath())
	if err != nil {
		return err
	}
	// check if content is same, no need for merge then (except for version)
	stin, err := t.model.GetInfo(relPath)
	if err != nil {
		log.Println("Merge: can not check if content is same!")
	} else {
		if stin.Content == msg.Object.Content {
			// log.Println("Core:", "Merge not required as updates are in sync!")
			// so all we need to do is apply the version update
			return t.model.ApplyModify(relPath, &msg.Object)
		}
	}
	// second: move to new name
	err = os.Rename(relPath.FullPath(), relPath.FullPath()+LOCAL)
	if err != nil {
		log.Println("Merge: original can not be found!")
		return err
	}
	// third: apply create of local version
	localVersionPath := relPath.RenameLastElement(relPath.LastElement() + LOCAL)
	err = t.model.ApplyCreate(localVersionPath, nil)
	if err != nil {
		log.Println("Merge: creating local merge file failed!")
		return err
	}
	// fourth: remove original
	err = t.model.ApplyRemove(relPath, nil)
	if err != nil {
		log.Println("Merge: removing original failed!")
		return err
	}
	// fifth: change path and apply remote as create
	msg.Operation = shared.OpCreate
	msg.Object.Path = relPath.SubPath() + REMOTE
	msg.Object.Name = relPath.LastElement() + REMOTE
	oldID := msg.Object.Identification
	msg.Object.Identification, err = shared.NewIdentifier()
	if err != nil {
		log.Println("Merge: failed to create new identifier!")
		return err
	}
	// new id --> rename temp file
	tempPath := t.Path + "/" + shared.TINZENITEDIR + "/" + shared.TEMPDIR
	err = os.Rename(tempPath+"/"+oldID, tempPath+"/"+msg.Object.Identification)
	if err != nil {
		log.Println("Merge: ipdating remote object file failed!")
		return err
	}
	// sixth: create remote file
	err = t.model.ApplyCreate(relPath.Apply(relPath.FullPath()+REMOTE), &msg.Object)
	if err != nil {
		log.Println("Merge: creating remote merge file failed!")
		return err
	}
	return nil
}

/*
initialize the background process.
*/
func (t *Tinzenite) initialize() {
	// prepare send channel that will distribute updates
	t.wg.Add(1)
	t.stop = make(chan bool, 1)
	t.sendChannel = make(chan shared.UpdateMessage, 1)
	go t.background()
	t.model.Register(t.sendChannel)
}

/*
background function that handles all async stuff that needs to be done.
*/
func (t *Tinzenite) background() {
	defer func() { log.Println("Tinzenite: Background process stopped.") }()
	// timer for long transfer feedback
	transferTicker := time.Tick(5 * time.Second)
	for {
		select {
		case <-t.stop:
			t.wg.Done()
			return
		case <-transferTicker:
			currentTransfers := t.channel.ActiveTransfers()
			// if currently none, done
			if len(currentTransfers) == 0 {
				continue
			}
			// find active transfer
			var currentProgress int
			for _, progress := range currentTransfers {
				if progress != 0 {
					currentProgress = progress
					break
				}
			}
			log.Printf("Tin: Pending %d transfers, current one at %d%%.\n", len(currentTransfers), currentProgress)
		case msg := <-t.sendChannel:
			// if muted don't send updates
			if t.muteFlag {
				continue
			}
			online, err := t.channel.OnlineAddresses()
			if err != nil {
				log.Println("Background:", err)
			}
			for _, address := range online {
				name := msg.Object.Name
				// for better visibility add special mark to signify directory
				if msg.Object.Directory {
					name += "/++"
				}
				log.Printf("Tin: sending <%s> of <.../%s> to %s.\n", msg.Operation, name, address[:8])
				t.channel.Send(address, msg.JSON())
			}
		} // select
	} // for
}
<commit_msg>peers are now updated in background process<commit_after>package core

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"sync"
	"time"

	"github.com/tinzenite/channel"
	"github.com/tinzenite/model"
	"github.com/tinzenite/shared"
)

/*
Tinzenite is the struct on which all important operations should be called.
*/
type Tinzenite struct {
	Path           string
	auth           *Authentication
	selfpeer       *shared.Peer
	channel        *channel.Channel
	cInterface     *chaninterface
	allPeers       []*shared.Peer
	model          *model.Model
	sendChannel    chan shared.UpdateMessage
	muteFlag       bool
	stop           chan bool
	wg             sync.WaitGroup
	peerValidation PeerValidation
}

/*
SyncRemote updates first locally and then sync remotely if other peers are
connected. NOTE: Both sync methods can block for a potentially long time,
especially when first run!
*/
func (t *Tinzenite) SyncRemote() error {
	// mute updates because we'll sync models later
	t.muteFlag = true
	// defer setting it back guaranteed
	defer func() { t.muteFlag = false }()
	// first ensure that local model is up to date
	err := t.SyncLocal()
	if err != nil {
		return err
	}
	// FIXME should iterate over peers instead
	online, err := t.channel.OnlineAddresses()
	if err != nil {
		return err
	}
	for _, address := range online {
		t.cInterface.SyncModel(address)
	}
	return nil
}

/*
SyncLocal changes. Will send updates to connected peers but not synchronize with
other peers.
*/
func (t *Tinzenite) SyncLocal() error {
	// update model
	return t.model.Update()
}

/*
Address of this Tinzenite peer that can be used to connect to.
*/
func (t *Tinzenite) Address() (string, error) {
	return t.channel.ConnectionAddress()
}

/*
Name of this Tinzenite peer.
*/
func (t *Tinzenite) Name() string {
	return t.selfpeer.Name
}

/*
Close cleanly stores everything and shuts Tinzenite down.
*/
func (t *Tinzenite) Close() {
	// send stop signal
	t.stop <- false
	// wait for it to close
	t.wg.Wait()
	// store all information
	t.Store()
	// FINALLY close (afterwards because I still need info from channel for store!)
	t.channel.Close()
}

/*
Store the tinzenite directory structure to disk. Will resolve all important
objects and store them so that it can later be reloaded. NOTE: Will not update
the full model, so be sure to have called Update() to guarantee an up to date
save.
*/
func (t *Tinzenite) Store() error {
	err := shared.MakeDotTinzenite(t.Path)
	if err != nil {
		return err
	}
	// write all peers to files
	for _, peer := range t.allPeers {
		err := peer.StoreTo(t.Path + "/" + shared.STOREPEERDIR)
		if err != nil {
			return err
		}
	}
	// store local peer info with toxdata
	toxData, err := t.channel.ToxData()
	if err != nil {
		return err
	}
	toxPeerDump := &shared.ToxPeerDump{
		SelfPeer: t.selfpeer,
		ToxData:  toxData}
	err = toxPeerDump.StoreTo(t.Path + "/" + shared.STORETOXDUMPDIR)
	if err != nil {
		return err
	}
	// store auth file
	err = t.auth.StoreTo(t.Path + "/" + shared.STOREAUTHDIR)
	if err != nil {
		return err
	}
	// update model for tinzenite dir to catch above stores
	err = t.model.PartialUpdate(t.Path + "/" + shared.TINZENITEDIR)
	if err != nil {
		return err
	}
	// finally store model (last because peers and org stuff is included!)
	return t.model.Store()
}

/*
PrintStatus returns a formatted string of the peer status.
*/
func (t *Tinzenite) PrintStatus() string {
	var out string
	out += "Online:\n"
	addresses, err := t.channel.FriendAddresses()
	if err != nil {
		out += "channel.FriendAddresses failed!"
	} else {
		var count int
		for _, address := range addresses {
			online, err := t.channel.IsAddressOnline(address)
			var insert string
			if err != nil {
				insert = "ERROR"
			} else {
				insert = fmt.Sprintf("%v", online)
			}
			out += address[:16] + " :: " + insert + "\n"
			count++
		}
		out += "Total friends: " + fmt.Sprintf("%d", count)
	}
	return out
}

/*
DisconnectPeer does exactly that. NOTE: this is a passive action and doesn't do
anything except remove the peer from the network. The peer is not further
notified.

TODO: maybe not use name but Identification?
TODO: when will other peers remove it? They need to remove the contact info from the channel... FIXME
*/
func (t *Tinzenite) DisconnectPeer(peerName string) {
	var newPeers []*shared.Peer
	for _, peer := range t.allPeers {
		if t.selfpeer.Identification == peer.Identification {
			continue
		}
		if peer.Name == peerName {
			log.Println("Removing", peer.Name, "at", peer.Address[:8])
			// delete peer file
			path := shared.CreatePath(t.Path, shared.TINZENITEDIR+"/"+shared.ORGDIR+"/"+shared.PEERSDIR+"/"+peer.Identification+shared.ENDING)
			err := t.model.ApplyRemove(path, nil)
			if err != nil {
				log.Println("DisconnectPeer:", err)
			}
			// remove from channel
			err = t.channel.RemoveConnection(peer.Address)
			if err != nil {
				log.Println("DisconnectPeer:", err)
			}
			// write peer to all removals so that no removals will be orphaned
			removePath := t.Path + "/" + shared.TINZENITEDIR + "/" + shared.REMOVEDIR
			allRemovals, _ := ioutil.ReadDir(removePath)
			// for every object that is currently being removed
			for _, stat := range allRemovals {
				// write the to be removed peer as done
				err := t.model.UpdateRemovalDir(stat.Name(), peer.Identification)
				if err != nil {
					// warn if it failed
					log.Println("Tinzenite: failed to purge removed peer from removal!")
				}
			}
			// continue does not readd to tinzenite, removing the reference to it
			continue
		}
		newPeers = append(newPeers, peer)
	}
	t.allPeers = newPeers
}

/*
applyPeers loads all peers and readies the communication channel accordingly.
*/
func (t *Tinzenite) applyPeers() error {
	peers, err := shared.LoadPeers(t.Path)
	if err != nil {
		return err
	}
	// make sure they are all tox ready
	for _, peer := range peers {
		// tox will return an error if the address has already been added, so we just ignore it
		_ = t.channel.AcceptConnection(peer.Address)
	}
	// TODO check initialized and online, start authentication FIXME
	// finally apply
	t.allPeers = peers
	return nil
}

/*
Send the given message to the peer with the address if online.
*/
func (t *Tinzenite) send(address, msg string) error {
	return t.channel.Send(address, msg)
}

/*
Merge an update message to the local model.
*/
func (t *Tinzenite) merge(msg *shared.UpdateMessage) error {
	relPath := shared.CreatePath(t.Path, msg.Object.Path)
	// first: apply local changes to model (this is why writing PartialUpdate was no waste of time, isn't this cool?! :D)
	err := t.model.PartialUpdate(relPath.FullPath())
	if err != nil {
		return err
	}
	// check if content is same, no need for merge then (except for version)
	stin, err := t.model.GetInfo(relPath)
	if err != nil {
		log.Println("Merge: can not check if content is same!")
	} else {
		if stin.Content == msg.Object.Content {
			// log.Println("Core:", "Merge not required as updates are in sync!")
			// so all we need to do is apply the version update
			return t.model.ApplyModify(relPath, &msg.Object)
		}
	}
	// second: move to new name
	err = os.Rename(relPath.FullPath(), relPath.FullPath()+LOCAL)
	if err != nil {
		log.Println("Merge: original can not be found!")
		return err
	}
	// third: apply create of local version
	localVersionPath := relPath.RenameLastElement(relPath.LastElement() + LOCAL)
	err = t.model.ApplyCreate(localVersionPath, nil)
	if err != nil {
		log.Println("Merge: creating local merge file failed!")
		return err
	}
	// fourth: remove original
	err = t.model.ApplyRemove(relPath, nil)
	if err != nil {
		log.Println("Merge: removing original failed!")
		return err
	}
	// fifth: change path and apply remote as create
	msg.Operation = shared.OpCreate
	msg.Object.Path = relPath.SubPath() + REMOTE
	msg.Object.Name = relPath.LastElement() + REMOTE
	oldID := msg.Object.Identification
	msg.Object.Identification, err = shared.NewIdentifier()
	if err != nil {
		log.Println("Merge: failed to create new identifier!")
		return err
	}
	// new id --> rename temp file
	tempPath := t.Path + "/" + shared.TINZENITEDIR + "/" + shared.TEMPDIR
	err = os.Rename(tempPath+"/"+oldID, tempPath+"/"+msg.Object.Identification)
	if err != nil {
		log.Println("Merge: ipdating remote object file failed!")
		return err
	}
	// sixth: create remote file
	err = t.model.ApplyCreate(relPath.Apply(relPath.FullPath()+REMOTE), &msg.Object)
	if err != nil {
		log.Println("Merge: creating remote merge file failed!")
		return err
	}
	return nil
}

/*
initialize the background process.
*/
func (t *Tinzenite) initialize() {
	// prepare send channel that will distribute updates
	t.wg.Add(1)
	t.stop = make(chan bool, 1)
	t.sendChannel = make(chan shared.UpdateMessage, 1)
	go t.background()
	t.model.Register(t.sendChannel)
}

/*
background function that handles all async stuff that needs to be done.
*/
func (t *Tinzenite) background() {
	defer func() { log.Println("Tinzenite: Background process stopped.") }()
	// timer for long transfer feedback
	transferTicker := time.Tick(5 * time.Second)
	// timer for peer management
	peerTicker := time.Tick(10 * time.Second)
	for {
		select {
		case <-t.stop:
			t.wg.Done()
			return
		case <-peerTicker:
			// update peers
			err := t.applyPeers()
			if err != nil {
				log.Println("Tin: error applying peers:", err)
			}
		case <-transferTicker:
			currentTransfers := t.channel.ActiveTransfers()
			// if currently none, done
			if len(currentTransfers) == 0 {
				continue
			}
			// find active transfer
			var currentProgress int
			for _, progress := range currentTransfers {
				if progress != 0 {
					currentProgress = progress
					break
				}
			}
			log.Printf("Tin: Pending %d transfers, current one at %d%%.\n", len(currentTransfers), currentProgress)
		case msg := <-t.sendChannel:
			// if muted don't send updates
			if t.muteFlag {
				continue
			}
			online, err := t.channel.OnlineAddresses()
			if err != nil {
				log.Println("Background:", err)
			}
			for _, address := range online {
				name := msg.Object.Name
				// for better visibility add special mark to signify directory
				if msg.Object.Directory {
					name += "/++"
				}
				log.Printf("Tin: sending <%s> of <.../%s> to %s.\n", msg.Operation, name, address[:8])
				t.channel.Send(address, msg.JSON())
			}
		} // select
	} // for
}
<|endoftext|>


<commit_before>// package tlsdialer contains a customized version of crypto/tls.Dial that
// allows control over whether or not to send the ServerName extension in the
// client handshake.
package tlsdialer

import (
	"crypto/x509"
	"fmt"
	"net"
	"time"

	"github.com/getlantern/golog"
	"github.com/getlantern/mtime"
	"github.com/getlantern/netx"
	"github.com/getlantern/ops"

	"github.com/refraction-networking/utls"
)

var (
	log = golog.LoggerFor("tlsdialer")
)

type timeoutError struct{}

func (timeoutError) Error() string   { return "tlsdialer: DialWithDialer timed out" }
func (timeoutError) Timeout() bool   { return true }
func (timeoutError) Temporary() bool { return true }

// Dialer is a configurable dialer that dials using tls
type Dialer struct {
	DoDial         func(net string, addr string, timeout time.Duration) (net.Conn, error)
	Timeout        time.Duration
	Network        string
	SendServerName bool
	ClientHelloID  tls.ClientHelloID
	Config         *tls.Config
}

// A tls.Conn along with timings for key steps in establishing that Conn
type ConnWithTimings struct {
	// Conn: the conn resulting from dialing
	Conn *tls.Conn
	// ResolutionTime: the amount of time it took to resolve the address
	ResolutionTime time.Duration
	// ConnectTime: the amount of time that it took to connect the socket
	ConnectTime time.Duration
	// HandshakeTime: the amount of time that it took to complete the TLS
	// handshake
	HandshakeTime time.Duration
	// ResolvedAddr: the address to which our dns lookup resolved
	ResolvedAddr *net.TCPAddr
	// VerifiedChains: like tls.ConnectionState.VerifiedChains
	VerifiedChains [][]*x509.Certificate
}

// Like crypto/tls.Dial, but with the ability to control whether or not to
// send the ServerName extension in client handshakes through the sendServerName
// flag.
//
// Note - if sendServerName is false, the VerifiedChains field on the
// connection's ConnectionState will never get populated. Use DialForTimings to
// get back a data structure that includes the verified chains.
func Dial(network, addr string, sendServerName bool, config *tls.Config) (*tls.Conn, error) {
	return DialTimeout(netx.DialTimeout, 1*time.Minute, network, addr, sendServerName, config)
}

// Like Dial, but timing out after the given timeout.
func DialTimeout(dial func(net string, addr string, timeout time.Duration) (net.Conn, error), timeout time.Duration, network, addr string, sendServerName bool, config *tls.Config) (*tls.Conn, error) {
	d := &Dialer{
		DoDial:         dial,
		Timeout:        timeout,
		SendServerName: sendServerName,
		Config:         config,
	}
	return d.Dial(network, addr)
}

// Like DialWithDialer but returns a data structure including timings and the
// verified chains.
func DialForTimings(dial func(net string, addr string, timeout time.Duration) (net.Conn, error), timeout time.Duration, network, addr string, sendServerName bool, config *tls.Config) (*ConnWithTimings, error) {
	d := &Dialer{
		DoDial:         dial,
		Timeout:        timeout,
		SendServerName: sendServerName,
		Config:         config,
	}
	return d.DialForTimings(network, addr)
}

// Dial dials the given network and address.
func (d *Dialer) Dial(network, addr string) (*tls.Conn, error) {
	cwt, err := d.DialForTimings(network, addr)
	if err != nil {
		return nil, err
	}
	return cwt.Conn, nil
}

// DialForTimings dials the given network and address and returns a ConnWithTimings.
func (d *Dialer) DialForTimings(network, addr string) (*ConnWithTimings, error) {
	result := &ConnWithTimings{}

	var errCh chan error

	if d.Timeout != 0 {
		// We want the Timeout and Deadline values to cover the whole process: TCP
		// connection and TLS handshake. This means that we also need to start our own
		// timers now.
		errCh = make(chan error, 10)
		time.AfterFunc(d.Timeout, func() {
			errCh <- timeoutError{}
		})
	}

	log.Tracef("Resolving addr: %s", addr)
	elapsed := mtime.Stopwatch()
	var err error
	if d.Timeout == 0 {
		log.Tracef("Resolving immediately")
		result.ResolvedAddr, err = netx.Resolve("tcp", addr)
	} else {
		log.Tracef("Resolving on goroutine")
		resolvedCh := make(chan *net.TCPAddr, 10)
		ops.Go(func() {
			resolved, resolveErr := netx.Resolve("tcp", addr)
			log.Tracef("Resolution resulted in %s : %s", resolved, resolveErr)
			resolvedCh <- resolved
			errCh <- resolveErr
		})
		err = <-errCh
		if err == nil {
			log.Tracef("No error, looking for resolved")
			result.ResolvedAddr = <-resolvedCh
		}
	}

	if err != nil {
		return result, err
	}
	result.ResolutionTime = elapsed()
	log.Tracef("Resolved addr %s to %s in %s", addr, result.ResolvedAddr, result.ResolutionTime)

	hostname, _, err := net.SplitHostPort(addr)
	if err != nil {
		return result, fmt.Errorf("Unable to split host and port for %v: %v", addr, err)
	}

	log.Tracef("Dialing %s %s (%s)", network, addr, result.ResolvedAddr)
	elapsed = mtime.Stopwatch()
	resolvedAddr := result.ResolvedAddr.String()
	rawConn, err := d.DoDial(network, resolvedAddr, d.Timeout)
	if err != nil {
		return result, err
	}
	result.ConnectTime = elapsed()
	log.Tracef("Dialed in %s", result.ConnectTime)

	config := d.Config
	if config == nil {
		config = &tls.Config{}
	}

	serverName := config.ServerName

	if serverName == "" {
		log.Trace("No ServerName set, inferring from the hostname to which we're connecting")
		serverName = hostname
	}
	log.Tracef("ServerName is: %s", serverName)

	log.Trace("Copying config so that we can tweak it")
	configCopy := new(tls.Config)
	*configCopy = *config

	if d.SendServerName {
		log.Tracef("Setting ServerName to %s and relying on the usual logic in tls.Conn.Handshake() to do verification", serverName)
		configCopy.ServerName = serverName
	} else {
		log.Trace("Clearing ServerName and disabling verification in tls.Conn.Handshake(). We'll verify manually after handshaking.")
		configCopy.ServerName = ""
		configCopy.InsecureSkipVerify = true
	}

	chid := d.ClientHelloID
	if chid.Client == "" {
		log.Trace("Defaulting to typical Golang client hello")
		chid = tls.HelloGolang
	}
	conn := tls.UClient(rawConn, configCopy, chid)
	if !d.SendServerName {
		// Actually omit the SNI Extension.
		// blank ServerName values appear to confuse some server software
		// and real browsers appear to omit the extension in this
		// case. XXX upstream fix?
		filteredExts := make([]tls.TLSExtension, 0, len(conn.Extensions))
		for _, e := range conn.Extensions {
			if _, ok := e.(*tls.SNIExtension); !ok {
				filteredExts = append(filteredExts, e)
			}
		}
		conn.Extensions = filteredExts
	}
	elapsed = mtime.Stopwatch()
	if d.Timeout == 0 {
		log.Trace("Handshaking immediately")
		err = conn.Handshake()
	} else {
		log.Trace("Handshaking on goroutine")
		ops.Go(func() {
			errCh <- conn.Handshake()
		})
		err = <-errCh
	}
	if err == nil {
		result.HandshakeTime = elapsed()
	}
	log.Tracef("Finished handshaking in: %s", result.HandshakeTime)

	if err == nil && !config.InsecureSkipVerify {
		if d.SendServerName {
			log.Trace("Depending on certificate verification in tls.Conn.Handshake()")
			result.VerifiedChains = conn.ConnectionState().VerifiedChains
		} else {
			log.Trace("Manually verifying certificates")
			configCopy.ServerName = ""
			result.VerifiedChains, err = verifyServerCerts(conn.Conn, serverName, configCopy)
		}
	}

	if err != nil {
		log.Tracef("Handshake or verification error, closing underlying connection: %v", err)
		if closeErr := rawConn.Close(); closeErr != nil {
			log.Debugf("Unable to close connection: %v", closeErr)
		}
		log.Errorf("Error establishing TLS connection to %v: %v", rawConn.RemoteAddr(), err)
		return result, err
	}

	result.Conn = conn.Conn
	return result, nil
}

func verifyServerCerts(conn *tls.Conn, serverName string, config *tls.Config) ([][]*x509.Certificate, error) {
	certs := conn.ConnectionState().PeerCertificates

	opts := x509.VerifyOptions{
		Roots:         config.RootCAs,
		CurrentTime:   time.Now(),
		DNSName:       serverName,
		Intermediates: x509.NewCertPool(),
	}

	for i, cert := range certs {
		if i == 0 {
			continue
		}
		opts.Intermediates.AddCert(cert)
	}
	return certs[0].Verify(opts)
}
<commit_msg>remove not-working sni extension filter (utls lazily creates this :P )<commit_after>// package tlsdialer contains a customized version of crypto/tls.Dial that
// allows control over whether or not to send the ServerName extension in the
// client handshake.
package tlsdialer

import (
	"crypto/x509"
	"fmt"
	"net"
	"time"

	"github.com/getlantern/golog"
	"github.com/getlantern/mtime"
	"github.com/getlantern/netx"
	"github.com/getlantern/ops"

	"github.com/refraction-networking/utls"
)

var (
	log = golog.LoggerFor("tlsdialer")
)

type timeoutError struct{}

func (timeoutError) Error() string   { return "tlsdialer: DialWithDialer timed out" }
func (timeoutError) Timeout() bool   { return true }
func (timeoutError) Temporary() bool { return true }

// Dialer is a configurable dialer that dials using tls
type Dialer struct {
	DoDial         func(net string, addr string, timeout time.Duration) (net.Conn, error)
	Timeout        time.Duration
	Network        string
	SendServerName bool
	ClientHelloID  tls.ClientHelloID
	Config         *tls.Config
}

// A tls.Conn along with timings for key steps in establishing that Conn
type ConnWithTimings struct {
	// Conn: the conn resulting from dialing
	Conn *tls.Conn
	// ResolutionTime: the amount of time it took to resolve the address
	ResolutionTime time.Duration
	// ConnectTime: the amount of time that it took to connect the socket
	ConnectTime time.Duration
	// HandshakeTime: the amount of time that it took to complete the TLS
	// handshake
	HandshakeTime time.Duration
	// ResolvedAddr: the address to which our dns lookup resolved
	ResolvedAddr *net.TCPAddr
	// VerifiedChains: like tls.ConnectionState.VerifiedChains
	VerifiedChains [][]*x509.Certificate
}

// Like crypto/tls.Dial, but with the ability to control whether or not to
// send the ServerName extension in client handshakes through the sendServerName
// flag.
//
// Note - if sendServerName is false, the VerifiedChains field on the
// connection's ConnectionState will never get populated. Use DialForTimings to
// get back a data structure that includes the verified chains.
func Dial(network, addr string, sendServerName bool, config *tls.Config) (*tls.Conn, error) {
	return DialTimeout(netx.DialTimeout, 1*time.Minute, network, addr, sendServerName, config)
}

// Like Dial, but timing out after the given timeout.
func DialTimeout(dial func(net string, addr string, timeout time.Duration) (net.Conn, error), timeout time.Duration, network, addr string, sendServerName bool, config *tls.Config) (*tls.Conn, error) {
	d := &Dialer{
		DoDial:         dial,
		Timeout:        timeout,
		SendServerName: sendServerName,
		Config:         config,
	}
	return d.Dial(network, addr)
}

// Like DialWithDialer but returns a data structure including timings and the
// verified chains.
func DialForTimings(dial func(net string, addr string, timeout time.Duration) (net.Conn, error), timeout time.Duration, network, addr string, sendServerName bool, config *tls.Config) (*ConnWithTimings, error) {
	d := &Dialer{
		DoDial:         dial,
		Timeout:        timeout,
		SendServerName: sendServerName,
		Config:         config,
	}
	return d.DialForTimings(network, addr)
}

// Dial dials the given network and address.
func (d *Dialer) Dial(network, addr string) (*tls.Conn, error) {
	cwt, err := d.DialForTimings(network, addr)
	if err != nil {
		return nil, err
	}
	return cwt.Conn, nil
}

// DialForTimings dials the given network and address and returns a ConnWithTimings.
func (d *Dialer) DialForTimings(network, addr string) (*ConnWithTimings, error) {
	result := &ConnWithTimings{}

	var errCh chan error

	if d.Timeout != 0 {
		// We want the Timeout and Deadline values to cover the whole process: TCP
		// connection and TLS handshake. This means that we also need to start our own
		// timers now.
		errCh = make(chan error, 10)
		time.AfterFunc(d.Timeout, func() {
			errCh <- timeoutError{}
		})
	}

	log.Tracef("Resolving addr: %s", addr)
	elapsed := mtime.Stopwatch()
	var err error
	if d.Timeout == 0 {
		log.Tracef("Resolving immediately")
		result.ResolvedAddr, err = netx.Resolve("tcp", addr)
	} else {
		log.Tracef("Resolving on goroutine")
		resolvedCh := make(chan *net.TCPAddr, 10)
		ops.Go(func() {
			resolved, resolveErr := netx.Resolve("tcp", addr)
			log.Tracef("Resolution resulted in %s : %s", resolved, resolveErr)
			resolvedCh <- resolved
			errCh <- resolveErr
		})
		err = <-errCh
		if err == nil {
			log.Tracef("No error, looking for resolved")
			result.ResolvedAddr = <-resolvedCh
		}
	}

	if err != nil {
		return result, err
	}
	result.ResolutionTime = elapsed()
	log.Tracef("Resolved addr %s to %s in %s", addr, result.ResolvedAddr, result.ResolutionTime)

	hostname, _, err := net.SplitHostPort(addr)
	if err != nil {
		return result, fmt.Errorf("Unable to split host and port for %v: %v", addr, err)
	}

	log.Tracef("Dialing %s %s (%s)", network, addr, result.ResolvedAddr)
	elapsed = mtime.Stopwatch()
	resolvedAddr := result.ResolvedAddr.String()
	rawConn, err := d.DoDial(network, resolvedAddr, d.Timeout)
	if err != nil {
		return result, err
	}
	result.ConnectTime = elapsed()
	log.Tracef("Dialed in %s", result.ConnectTime)

	config := d.Config
	if config == nil {
		config = &tls.Config{}
	}

	serverName := config.ServerName

	if serverName == "" {
		log.Trace("No ServerName set, inferring from the hostname to which we're connecting")
		serverName = hostname
	}
	log.Tracef("ServerName is: %s", serverName)

	log.Trace("Copying config so that we can tweak it")
	configCopy := new(tls.Config)
	*configCopy = *config

	if d.SendServerName {
		log.Tracef("Setting ServerName to %s and relying on the usual logic in tls.Conn.Handshake() to do verification", serverName)
		configCopy.ServerName = serverName
	} else {
		log.Trace("Clearing ServerName and disabling verification in tls.Conn.Handshake(). We'll verify manually after handshaking.")
		configCopy.ServerName = ""
		configCopy.InsecureSkipVerify = true
	}

	chid := d.ClientHelloID
	if chid.Client == "" {
		log.Trace("Defaulting to typical Golang client hello")
		chid = tls.HelloGolang
	}
	conn := tls.UClient(rawConn, configCopy, chid)
	elapsed = mtime.Stopwatch()
	if d.Timeout == 0 {
		log.Trace("Handshaking immediately")
		err = conn.Handshake()
	} else {
		log.Trace("Handshaking on goroutine")
		ops.Go(func() {
			errCh <- conn.Handshake()
		})
		err = <-errCh
	}
	if err == nil {
		result.HandshakeTime = elapsed()
	}
	log.Tracef("Finished handshaking in: %s", result.HandshakeTime)

	if err == nil && !config.InsecureSkipVerify {
		if d.SendServerName {
			log.Trace("Depending on certificate verification in tls.Conn.Handshake()")
			result.VerifiedChains = conn.ConnectionState().VerifiedChains
		} else {
			log.Trace("Manually verifying certificates")
			configCopy.ServerName = ""
			result.VerifiedChains, err = verifyServerCerts(conn.Conn, serverName, configCopy)
		}
	}

	if err != nil {
		log.Tracef("Handshake or verification error, closing underlying connection: %v", err)
		if closeErr := rawConn.Close(); closeErr != nil {
			log.Debugf("Unable to close connection: %v", closeErr)
		}
		log.Errorf("Error establishing TLS connection to %v: %v", rawConn.RemoteAddr(), err)
		return result, err
	}

	result.Conn = conn.Conn
	return result, nil
}

func verifyServerCerts(conn *tls.Conn, serverName string, config *tls.Config) ([][]*x509.Certificate, error) {
	certs := conn.ConnectionState().PeerCertificates

	opts := x509.VerifyOptions{
		Roots:         config.RootCAs,
		CurrentTime:   time.Now(),
		DNSName:       serverName,
		Intermediates: x509.NewCertPool(),
	}

	for i, cert := range certs {
		if i == 0 {
			continue
		}
		opts.Intermediates.AddCert(cert)
	}
	return certs[0].Verify(opts)
}
<|endoftext|>


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

package tcp_test

import (
	"math/rand"
	"net"
	"os"
	"runtime"
	"testing"

	"github.com/mikioh/tcp"
)

func TestOptionWithVariousBufferLengths(t *testing.T) {
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()
	c, err := net.Dial(ln.Addr().Network(), ln.Addr().String())
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	tc, err := tcp.NewConn(c)
	if err != nil {
		t.Fatal(err)
	}
	for i := 0; i < 256; i++ {
		level, name := rand.Int(), rand.Int()
		b := make([]byte, i)
		tc.Option(level, name, b)
	}
}

func TestOriginalDst(t *testing.T) {
	switch runtime.GOOS {
	case "linux":
	case "darwin", "dragonfly", "freebsd", "openbsd":
		if os.Getuid() != 0 {
			t.Skip("must be root")
		}
	default:
		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
	}

	for _, address := range []string{"127.0.0.1:0"} {
		ln, err := net.Listen("tcp", address)
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		c, err := net.Dial(ln.Addr().Network(), ln.Addr().String())
		if err != nil {
			t.Fatal(err)
		}
		defer c.Close()

		tc, err := tcp.NewConn(c)
		if err != nil {
			t.Fatal(err)
		}
		if _, err := tc.OriginalDst(); err != nil {
			t.Fatal(err)
		}
	}
}
<commit_msg>tcp: don't fail TestOriginalDst on PF unconfigured hosts<commit_after>// Copyright 2016 Mikio Hara. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tcp_test

import (
	"math/rand"
	"net"
	"os"
	"runtime"
	"testing"

	"github.com/mikioh/tcp"
)

func TestOptionWithVariousBufferLengths(t *testing.T) {
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()
	c, err := net.Dial(ln.Addr().Network(), ln.Addr().String())
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	tc, err := tcp.NewConn(c)
	if err != nil {
		t.Fatal(err)
	}
	for i := 0; i < 256; i++ {
		level, name := rand.Int(), rand.Int()
		b := make([]byte, i)
		tc.Option(level, name, b)
	}
}

func TestOriginalDst(t *testing.T) {
	switch runtime.GOOS {
	case "linux":
	case "darwin", "dragonfly", "freebsd", "openbsd":
		if os.Getuid() != 0 {
			t.Skip("must be root")
		}
	default:
		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
	}

	for _, address := range []string{"127.0.0.1:0"} {
		ln, err := net.Listen("tcp", address)
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		c, err := net.Dial(ln.Addr().Network(), ln.Addr().String())
		if err != nil {
			t.Fatal(err)
		}
		defer c.Close()

		tc, err := tcp.NewConn(c)
		if err != nil {
			t.Fatal(err)
		}
		if _, err := tc.OriginalDst(); err != nil {
			t.Log(err)
		}
	}
}
<|endoftext|>


<commit_before>package ndp_test

import (
	"net"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/mdlayher/ndp"
)

func TestLinkLayerAddressMarshalUnmarshalBinary(t *testing.T) {
	addr := net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad}

	tests := []struct {
		name string
		lla  *ndp.LinkLayerAddress
		b    []byte
		ok   bool
	}{
		{
			name: "bad, invalid direction",
			lla: &ndp.LinkLayerAddress{
				Direction: 10,
			},
		},
		{
			name: "bad, invalid address",
			lla: &ndp.LinkLayerAddress{
				Direction: ndp.Source,
				Addr:      net.HardwareAddr{0xde, 0xad, 0xbe, 0xef},
			},
		},
		{
			name: "ok, source",
			lla: &ndp.LinkLayerAddress{
				Direction: ndp.Source,
				Addr:      addr,
			},
			b: append([]byte{
				0x01,
				0x01,
			}, addr...),
			ok: true,
		},
		{
			name: "ok, target",
			lla: &ndp.LinkLayerAddress{
				Direction: ndp.Target,
				Addr:      addr,
			},
			b: append([]byte{
				0x02,
				0x01,
			}, addr...),
			ok: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b, err := tt.lla.MarshalBinary()

			if err != nil && tt.ok {
				t.Fatalf("unexpected error: %v", err)
			}
			if err == nil && !tt.ok {
				t.Fatal("expected an error, but none occurred")
			}
			if err != nil {
				t.Logf("OK error: %v", err)
				return
			}

			if diff := cmp.Diff(tt.b, b); diff != "" {
				t.Fatalf("unexpected Option bytes (-want +got):\n%s", diff)
			}

			lla := new(ndp.LinkLayerAddress)
			if err := lla.UnmarshalBinary(b); err != nil {
				t.Fatalf("failed to unmarshal binary: %v", err)
			}

			if diff := cmp.Diff(tt.lla, lla); diff != "" {
				t.Fatalf("unexpected link-layer address (-want +got):\n%s", diff)
			}
		})
	}
}

func TestLinkLayerAddressUnmarshalBinary(t *testing.T) {
	addr := net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad}

	tests := []struct {
		name string
		b    []byte
		lla  *ndp.LinkLayerAddress
		ok   bool
	}{
		{
			name: "bad, short",
			b:    addr,
		},
		{
			name: "bad, invalid direction",
			b: append([]byte{
				0x10,
				0x01,
			}, addr...),
		},
		{
			name: "bad, invalid length",
			b: append([]byte{
				0x01,
				0x10,
			}, addr...),
		},
		{
			name: "ok, source",
			b: append([]byte{
				0x01,
				0x01,
			}, addr...),
			lla: &ndp.LinkLayerAddress{
				Direction: ndp.Source,
				Addr:      addr,
			},
			ok: true,
		},
		{
			name: "ok, target",
			b: append([]byte{
				0x02,
				0x01,
			}, addr...),
			lla: &ndp.LinkLayerAddress{
				Direction: ndp.Target,
				Addr:      addr,
			},
			ok: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			lla := new(ndp.LinkLayerAddress)
			err := lla.UnmarshalBinary(tt.b)

			if err != nil && tt.ok {
				t.Fatalf("unexpected error: %v", err)
			}
			if err == nil && !tt.ok {
				t.Fatal("expected an error, but none occurred")
			}
			if err != nil {
				t.Logf("OK error: %v", err)
				return
			}

			if diff := cmp.Diff(tt.lla, lla); diff != "" {
				t.Fatalf("unexpected link-layer address (-want +got):\n%s", diff)
			}
		})
	}
}
<commit_msg>ndp: clean up options tests with byte merge<commit_after>package ndp_test

import (
	"net"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/mdlayher/ndp"
)

func TestLinkLayerAddressMarshalUnmarshalBinary(t *testing.T) {
	addr := net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad}

	tests := []struct {
		name string
		lla  *ndp.LinkLayerAddress
		bs   [][]byte
		ok   bool
	}{
		{
			name: "bad, invalid direction",
			lla: &ndp.LinkLayerAddress{
				Direction: 10,
			},
		},
		{
			name: "bad, invalid address",
			lla: &ndp.LinkLayerAddress{
				Direction: ndp.Source,
				Addr:      net.HardwareAddr{0xde, 0xad, 0xbe, 0xef},
			},
		},
		{
			name: "ok, source",
			lla: &ndp.LinkLayerAddress{
				Direction: ndp.Source,
				Addr:      addr,
			},
			bs: [][]byte{
				{0x01, 0x01},
				addr,
			},
			ok: true,
		},
		{
			name: "ok, target",
			lla: &ndp.LinkLayerAddress{
				Direction: ndp.Target,
				Addr:      addr,
			},
			bs: [][]byte{
				{0x02, 0x01},
				addr,
			},
			ok: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b, err := tt.lla.MarshalBinary()

			if err != nil && tt.ok {
				t.Fatalf("unexpected error: %v", err)
			}
			if err == nil && !tt.ok {
				t.Fatal("expected an error, but none occurred")
			}
			if err != nil {
				t.Logf("OK error: %v", err)
				return
			}

			ttb := merge(tt.bs)
			if diff := cmp.Diff(ttb, b); diff != "" {
				t.Fatalf("unexpected Option bytes (-want +got):\n%s", diff)
			}

			lla := new(ndp.LinkLayerAddress)
			if err := lla.UnmarshalBinary(b); err != nil {
				t.Fatalf("failed to unmarshal binary: %v", err)
			}

			if diff := cmp.Diff(tt.lla, lla); diff != "" {
				t.Fatalf("unexpected link-layer address (-want +got):\n%s", diff)
			}
		})
	}
}

func TestLinkLayerAddressUnmarshalBinary(t *testing.T) {
	addr := net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad}

	tests := []struct {
		name string
		bs   [][]byte
		lla  *ndp.LinkLayerAddress
		ok   bool
	}{
		{
			name: "bad, short",
			bs:   [][]byte{addr},
		},
		{
			name: "bad, invalid direction",
			bs: [][]byte{
				{0x10, 0x01},
				addr,
			},
		},
		{
			name: "bad, invalid length",
			bs: [][]byte{
				{0x01, 0x10},
				addr,
			},
		},
		{
			name: "ok, source",
			bs: [][]byte{
				{0x01, 0x01},
				addr,
			},
			lla: &ndp.LinkLayerAddress{
				Direction: ndp.Source,
				Addr:      addr,
			},
			ok: true,
		},
		{
			name: "ok, target",
			bs: [][]byte{
				{0x02, 0x01},
				addr,
			},
			lla: &ndp.LinkLayerAddress{
				Direction: ndp.Target,
				Addr:      addr,
			},
			ok: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			lla := new(ndp.LinkLayerAddress)
			err := lla.UnmarshalBinary(merge(tt.bs))

			if err != nil && tt.ok {
				t.Fatalf("unexpected error: %v", err)
			}
			if err == nil && !tt.ok {
				t.Fatal("expected an error, but none occurred")
			}
			if err != nil {
				t.Logf("OK error: %v", err)
				return
			}

			if diff := cmp.Diff(tt.lla, lla); diff != "" {
				t.Fatalf("unexpected link-layer address (-want +got):\n%s", diff)
			}
		})
	}
}
<|endoftext|>


<commit_before>// Package tool implements a framework for constructing multi-purpose
// command-line programs in the fashion of the `go` tool.
package tool

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
)

// Description is the description that is printed in the main usage message. If
// set, the text should begin and end with a newline.
var Description string

// Command is a subcommand of the main tool.
type Command struct {
	Name        string
	Summary     string // one-line description for command list
	Usage       string // metavariables displayed in usage message
	Description string // block description for usage message
	Function    func([]string)
	FlagSet     *flag.FlagSet
	MinArgs     int
	MaxArgs     int // -1 denotes no maximum
	HasOpts     bool
}

// Commands is the list of subcommands available to the program.
var Commands = make(map[string]*Command)

// UsageFunc constructs a FlagSet.Usage function from a Command.
func UsageFunc(cmd *Command) func() {
	return func() {
		fmt.Fprintf(os.Stderr, "Usage: %s %s %s\n",
			os.Args[0], cmd.Name, cmd.Usage)
		fmt.Fprint(os.Stderr, cmd.Description)
		if cmd.HasOpts {
			fmt.Fprintln(os.Stderr, "\nOptions:")
			cmd.FlagSet.PrintDefaults()
		}
	}
}

func readStdin(args []string, max int) []string {
	r := bufio.NewReader(os.Stdin)
	for max < 0 || len(args) < max {
		line, err := r.ReadString('\n')
		if len(line) > 0 {
			args = append(args, string(line[:len(line)-1]))
		}
		if err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		}
	}
	return args
}

func getArgs(cmd string, min, max int, args []string) []string {
	if max >= 0 && len(args) > max {
		fmt.Fprintf(os.Stderr, "%s: %s: too many arguments\n", os.Args[0], cmd)
		os.Exit(2)
	}

	if len(args) < min {
		args = readStdin(args, max)
	}
	if len(args) < min {
		fmt.Fprintf(os.Stderr, "%s: %s: missing argument\n", os.Args[0],
			cmd)
		os.Exit(2)
	}

	return args
}

func printCommands() {
	maxlen := 0
	for _, cmd := range Commands {
		if len(cmd.Name) > maxlen {
			maxlen = len(cmd.Name)
		}
	}
	space := "                "

	for _, cmd := range Commands {
		fmt.Fprintf(os.Stderr, "  %s%s  %s\n", cmd.Name,
			space[:maxlen-len(cmd.Name)], cmd.Summary)
	}
}

func parseFlags() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s <cmd> [<arg>]...\n", os.Args[0])
		fmt.Fprint(os.Stderr, Description)
		fmt.Fprintf(os.Stderr, `
If not enough command-line arguments are specified for a command,
remaining arguments are read from standard input. For help regarding a
specific command, see '%s <cmd> -h'.
`, os.Args[0])
		fmt.Fprint(os.Stderr, "\nCommands:\n")
		printCommands()
		os.Exit(2)
	}

	flag.Parse()

	if flag.NArg() < 1 {
		flag.Usage()
	}
}

// Main runs the tool. Commands should be initialized before calling this
// function.
func Main() {
	log.SetFlags(0)
	log.SetPrefix(fmt.Sprintf("%s: ", os.Args[0]))

	parseFlags()

	if cmd := Commands[flag.Arg(0)]; cmd != nil {
		log.SetPrefix(fmt.Sprintf("%s: %s: ", os.Args[0], cmd.Name))
		if err := cmd.FlagSet.Parse(flag.Args()[1:]); err == flag.ErrHelp {
			cmd.FlagSet.Usage()
			os.Exit(2)
		} else if err != nil {
			log.Fatal(err)
		}
		args := getArgs(cmd.Name, cmd.MinArgs, cmd.MaxArgs, cmd.FlagSet.Args())
		cmd.Function(args)
	} else {
		fmt.Fprintf(os.Stderr, "%s: no such command: %s\n", os.Args[0],
			flag.Arg(0))
		os.Exit(2)
	}
}
<commit_msg>Sort subcommands in tool package help message<commit_after>// Package tool implements a framework for constructing multi-purpose
// command-line programs in the fashion of the `go` tool.
package tool

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
)

// Description is the description that is printed in the main usage message. If
// set, the text should begin and end with a newline.
var Description string

// Command is a subcommand of the main tool.
type Command struct {
	Name        string
	Summary     string // one-line description for command list
	Usage       string // metavariables displayed in usage message
	Description string // block description for usage message
	Function    func([]string)
	FlagSet     *flag.FlagSet
	MinArgs     int
	MaxArgs     int // -1 denotes no maximum
	HasOpts     bool
}

// Commands is the list of subcommands available to the program.
var Commands = make(map[string]*Command)

// UsageFunc constructs a FlagSet.Usage function from a Command.
func UsageFunc(cmd *Command) func() {
	return func() {
		fmt.Fprintf(os.Stderr, "Usage: %s %s %s\n",
			os.Args[0], cmd.Name, cmd.Usage)
		fmt.Fprint(os.Stderr, cmd.Description)
		if cmd.HasOpts {
			fmt.Fprintln(os.Stderr, "\nOptions:")
			cmd.FlagSet.PrintDefaults()
		}
	}
}

func readStdin(args []string, max int) []string {
	r := bufio.NewReader(os.Stdin)
	for max < 0 || len(args) < max {
		line, err := r.ReadString('\n')
		if len(line) > 0 {
			args = append(args, string(line[:len(line)-1]))
		}
		if err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		}
	}
	return args
}

func getArgs(cmd string, min, max int, args []string) []string {
	if max >= 0 && len(args) > max {
		fmt.Fprintf(os.Stderr, "%s: %s: too many arguments\n", os.Args[0], cmd)
		os.Exit(2)
	}

	if len(args) < min {
		args = readStdin(args, max)
	}
	if len(args) < min {
		fmt.Fprintf(os.Stderr, "%s: %s: missing argument\n", os.Args[0],
			cmd)
		os.Exit(2)
	}

	return args
}

func printCommands() {
	names := make([]string, len(Commands))
	i := 0
	for name, _ := range Commands {
		names[i] = name
		i++
	}
	sort.Strings(names)

	maxlen := 0
	for _, name := range names {
		if len(name) > maxlen {
			maxlen = len(name)
		}
	}
	space := "                "

	for _, name := range names {
		fmt.Fprintf(os.Stderr, "  %s%s  %s\n", name, space[:maxlen-len(name)],
			Commands[name].Summary)
	}
}

func parseFlags() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s <cmd> [<arg>]...\n", os.Args[0])
		fmt.Fprint(os.Stderr, Description)
		fmt.Fprintf(os.Stderr, `
If not enough command-line arguments are specified for a command,
remaining arguments are read from standard input. For help regarding a
specific command, see '%s <cmd> -h'.
`, os.Args[0])
		fmt.Fprint(os.Stderr, "\nCommands:\n")
		printCommands()
		os.Exit(2)
	}

	flag.Parse()

	if flag.NArg() < 1 {
		flag.Usage()
	}
}

// Main runs the tool. Commands should be initialized before calling this
// function.
func Main() {
	log.SetFlags(0)
	log.SetPrefix(fmt.Sprintf("%s: ", os.Args[0]))

	parseFlags()

	if cmd := Commands[flag.Arg(0)]; cmd != nil {
		log.SetPrefix(fmt.Sprintf("%s: %s: ", os.Args[0], cmd.Name))
		if err := cmd.FlagSet.Parse(flag.Args()[1:]); err == flag.ErrHelp {
			cmd.FlagSet.Usage()
			os.Exit(2)
		} else if err != nil {
			log.Fatal(err)
		}
		args := getArgs(cmd.Name, cmd.MinArgs, cmd.MaxArgs, cmd.FlagSet.Args())
		cmd.Function(args)
	} else {
		fmt.Fprintf(os.Stderr, "%s: no such command: %s\n", os.Args[0],
			flag.Arg(0))
		os.Exit(2)
	}
}
<|endoftext|>