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

package main

import (
	"bufio"
	"context"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"time"

	perfetto_pb "perfetto/config"

	"github.com/golang/protobuf/proto"
	"github.com/google/gapid/core/app"
	"github.com/google/gapid/core/app/crash"
	"github.com/google/gapid/core/event/task"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/device"
	"github.com/google/gapid/gapis/service"
	"github.com/google/gapid/gapis/service/path"
)

type traceVerb struct{ TraceFlags }

func init() {
	verb := &traceVerb{}
	verb.TraceFlags.Disable.PCS = true

	app.AddVerb(&app.Verb{
		Name:      "trace",
		ShortHelp: "Captures a gfx trace from an application",
		Action:    verb,
	})
}

type target func(opts *service.TraceOptions)

func (verb *traceVerb) Run(ctx context.Context, flags flag.FlagSet) error {
	if flags.NArg() > 1 {
		app.Usage(ctx, "Expected at most one argument.")
		return nil
	}

	if verb.API == "" {
		app.Usage(ctx, "The API is required.")
		return nil
	}
	api, err := verb.apiAndType()
	if err != nil {
		return err
	}

	traceURI := verb.URI
	if traceURI == "" && verb.Local.Port == 0 {
		if flags.NArg() != 1 {
			if api.traceType != service.TraceType_Perfetto {
				app.Usage(ctx, "Expected application name.")
				return nil
			}
		} else {
			traceURI = flags.Arg(0)
		}
	} else if flags.NArg() != 0 {
		app.Usage(ctx, "Expected no arguments when a URI or port is specified.")
		return nil
	}

	if api.traceType == service.TraceType_Perfetto {
		if verb.Perfetto == "" {
			app.Usage(ctx, "The Perfetto config is required for System Profiles.")
			return nil
		}
		if verb.Local.Port != 0 {
			app.Usage(ctx, "-local-port is not supported for System Profiles.")
			return nil
		}
	}

	client, err := getGapis(ctx, verb.Gapis, GapirFlags{})
	if err != nil {
		return log.Err(ctx, err, "Failed to connect to the GAPIS server")
	}
	defer client.Close()

	out := "trace" + api.traceExt
	var target target

	if verb.Local.Port != 0 {
		serverInfo, err := client.GetServerInfo(ctx)
		if err != nil {
			return err
		}
		traceDevice := serverInfo.GetServerLocalDevice()
		if traceDevice.GetID() == nil {
			return fmt.Errorf("The server was not started with a local device for tracing")
		}
		target = func(opts *service.TraceOptions) {
			opts.Device = traceDevice
			opts.App = &service.TraceOptions_Port{
				Port: uint32(verb.Local.Port),
			}
		}
	} else {
		// Find the actual trace URI from all of the devices
		devices, err := filterDevices(ctx, &verb.DeviceFlags, client)
		if err != nil {
			return err
		}

		if len(devices) == 0 {
			return fmt.Errorf("Could not find matching device")
		}

		if len(devices) == 1 && strings.HasPrefix(traceURI, "port:") {
			target = func(opts *service.TraceOptions) {
				opts.Device = devices[0]
				opts.App = &service.TraceOptions_Uri{
					Uri: traceURI,
				}
			}
		} else if len(devices) == 1 && strings.HasPrefix(traceURI, "apk:") {
			data, err := ioutil.ReadFile(traceURI[4:])
			if err != nil {
				return log.Errf(ctx, err, "Failed to read APK at %s", traceURI[4:])
			}
			target = func(opts *service.TraceOptions) {
				opts.Device = devices[0]
				opts.App = &service.TraceOptions_UploadApplication{
					UploadApplication: data,
				}
			}
		} else if traceURI == "" {
			if len(devices) != 1 {
				return log.Errf(ctx, nil, "Found multiple matching devices, please specify the trace device")
			}
			target = func(opts *service.TraceOptions) {
				opts.Device = devices[0]
			}
		} else {
			type info struct {
				uri        string
				device     *path.Device
				deviceName string
				name       string
			}
			var found []info

			for _, dev := range devices {
				targets, err := client.FindTraceTargets(ctx, &service.FindTraceTargetsRequest{
					Device: dev,
					Uri:    traceURI,
				})
				if err != nil {
					continue
				}

				dd, err := client.Get(ctx, dev.Path(), nil)
				if err != nil {
					return err
				}
				d := dd.(*device.Instance)

				for _, target := range targets {
					name := target.Name
					switch {
					case target.FriendlyApplication != "":
						name = target.FriendlyApplication
					case target.FriendlyExecutable != "":
						name = target.FriendlyExecutable
					}

					found = append(found, info{
						uri:        target.Uri,
						deviceName: d.Name,
						device:     dev,
						name:       name,
					})
				}
			}

			if len(found) == 0 {
				return fmt.Errorf("Could not find %+v to trace on any device", traceURI)
			}

			if len(found) > 1 {
				sb := strings.Builder{}
				fmt.Fprintf(&sb, "Found %v candidates: \n", traceURI)
				for i, f := range found {
					if i == 0 || found[i-1].deviceName != f.deviceName {
						fmt.Fprintf(&sb, "  %v:\n", f.deviceName)
					}
					fmt.Fprintf(&sb, "    %v\n", f.uri)
				}
				return log.Errf(ctx, nil, "%v", sb.String())
			}

			fmt.Printf("Tracing %+v\n", found[0].uri)
			out = found[0].name + api.traceExt
			target = func(opts *service.TraceOptions) {
				opts.Device = found[0].device
				opts.App = &service.TraceOptions_Uri{
					Uri: found[0].uri,
				}
			}
		}
	}

	if verb.Out != "" {
		out = verb.Out
	}

	options := &service.TraceOptions{
		Type:                         api.traceType,
		Apis:                         api.apis,
		AdditionalCommandLineArgs:    verb.AdditionalArgs,
		Cwd:                          verb.WorkingDir,
		Environment:                  verb.Env,
		Duration:                     float32(verb.For.Seconds()),
		ObserveFrameFrequency:        uint32(verb.Observe.Frames),
		ObserveDrawFrequency:         uint32(verb.Observe.Draws),
		StartFrame:                   uint32(verb.Start.At.Frame),
		FramesToCapture:              uint32(verb.Capture.Frames),
		DisablePcs:                   verb.Disable.PCS,
		RecordErrorState:             verb.Record.Errors,
		DeferStart:                   verb.Start.Defer,
		NoBuffer:                     verb.No.Buffer,
		HideUnknownExtensions:        verb.Disable.Unknown.Extensions,
		RecordTraceTimes:             verb.Record.TraceTimes,
		ClearCache:                   verb.Clear.Cache,
		ServerLocalSavePath:          out,
		PipeName:                     verb.PipeName,
		DisableCoherentMemoryTracker: verb.Disable.CoherentMemoryTracker,
	}
	target(options)

	if api.traceType == service.TraceType_Perfetto {
		data, err := ioutil.ReadFile(verb.Perfetto)
		if err != nil {
			return log.Errf(ctx, err, "Failed to read Perfetto config")
		}
		options.PerfettoConfig = &perfetto_pb.TraceConfig{}
		if err := proto.UnmarshalText(string(data), options.PerfettoConfig); err != nil {
			return log.Errf(ctx, err, "Failed to parse Perfetto config")
		}
		dur := uint32(verb.For.Seconds() * 1000)
		if dur == 0 {
			dur = 10 * 60 * 1000
		}
		options.PerfettoConfig.DurationMs = proto.Uint32(dur)
		options.Duration = 0
	}

	handler, err := client.Trace(ctx)
	if err != nil {
		return err
	}
	defer handler.Dispose(ctx)

	defer app.AddInterruptHandler(func() {
		handler.Dispose(ctx)
	})()

	status, err := handler.Initialize(ctx, options)
	if err != nil {
		return err
	}
	log.I(ctx, "Trace Status %+v", status)

	handlerInstalled := options.Duration > 0

	return task.Retry(ctx, 0, time.Second*3, func(ctx context.Context) (retry bool, err error) {
		status, err = handler.Event(ctx, service.TraceEvent_Status)
		if err == io.EOF {
			return true, nil
		}
		if err != nil {
			log.I(ctx, "Error %+v", err)
			return true, err
		}
		if status == nil {
			return true, nil
		}

		if status.BytesCaptured > 0 {
			if !handlerInstalled {
				crash.Go(func() {
					reader := bufio.NewReader(os.Stdin)
					if options.DeferStart {
						println("Press enter to start capturing...")
						_, _ = reader.ReadString('\n')
						_, _ = handler.Event(ctx, service.TraceEvent_Begin)
					}
					println("Press enter to stop capturing...")
					_, _ = reader.ReadString('\n')
					handler.Event(ctx, service.TraceEvent_Stop)
				})
				handlerInstalled = true
			}
			log.I(ctx, "Capturing %+v", status.BytesCaptured)
		}
		if status.Status == service.TraceStatus_Done {
			return true, nil
		}
		return false, nil
	})
}

type apiAndType struct {
	traceType service.TraceType
	apis      []string
	traceExt  string
}

func (verb *traceVerb) apiAndType() (apiAndType, error) {
	switch verb.API {
	case "vulkan":
		return apiAndType{
			service.TraceType_Graphics,
			[]string{"Vulkan"},
			".gfxtrace",
		}, nil
	case "gles":
		return apiAndType{
			service.TraceType_Graphics,
			[]string{"OpenGLES"},
			".gfxtrace",
		}, nil
	case "perfetto":
		return apiAndType{
			service.TraceType_Perfetto,
			[]string{},
			".perfetto",
		}, nil
	default:
		return apiAndType{}, fmt.Errorf("Unknown API '%s'", verb.API)
	}
}
<commit_msg>Improve gapit trace message: add "bytes" unit (#3212)<commit_after>// Copyright (C) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bufio"
	"context"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"time"

	perfetto_pb "perfetto/config"

	"github.com/golang/protobuf/proto"
	"github.com/google/gapid/core/app"
	"github.com/google/gapid/core/app/crash"
	"github.com/google/gapid/core/event/task"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/device"
	"github.com/google/gapid/gapis/service"
	"github.com/google/gapid/gapis/service/path"
)

type traceVerb struct{ TraceFlags }

func init() {
	verb := &traceVerb{}
	verb.TraceFlags.Disable.PCS = true

	app.AddVerb(&app.Verb{
		Name:      "trace",
		ShortHelp: "Captures a gfx trace from an application",
		Action:    verb,
	})
}

type target func(opts *service.TraceOptions)

func (verb *traceVerb) Run(ctx context.Context, flags flag.FlagSet) error {
	if flags.NArg() > 1 {
		app.Usage(ctx, "Expected at most one argument.")
		return nil
	}

	if verb.API == "" {
		app.Usage(ctx, "The API is required.")
		return nil
	}
	api, err := verb.apiAndType()
	if err != nil {
		return err
	}

	traceURI := verb.URI
	if traceURI == "" && verb.Local.Port == 0 {
		if flags.NArg() != 1 {
			if api.traceType != service.TraceType_Perfetto {
				app.Usage(ctx, "Expected application name.")
				return nil
			}
		} else {
			traceURI = flags.Arg(0)
		}
	} else if flags.NArg() != 0 {
		app.Usage(ctx, "Expected no arguments when a URI or port is specified.")
		return nil
	}

	if api.traceType == service.TraceType_Perfetto {
		if verb.Perfetto == "" {
			app.Usage(ctx, "The Perfetto config is required for System Profiles.")
			return nil
		}
		if verb.Local.Port != 0 {
			app.Usage(ctx, "-local-port is not supported for System Profiles.")
			return nil
		}
	}

	client, err := getGapis(ctx, verb.Gapis, GapirFlags{})
	if err != nil {
		return log.Err(ctx, err, "Failed to connect to the GAPIS server")
	}
	defer client.Close()

	out := "trace" + api.traceExt
	var target target

	if verb.Local.Port != 0 {
		serverInfo, err := client.GetServerInfo(ctx)
		if err != nil {
			return err
		}
		traceDevice := serverInfo.GetServerLocalDevice()
		if traceDevice.GetID() == nil {
			return fmt.Errorf("The server was not started with a local device for tracing")
		}
		target = func(opts *service.TraceOptions) {
			opts.Device = traceDevice
			opts.App = &service.TraceOptions_Port{
				Port: uint32(verb.Local.Port),
			}
		}
	} else {
		// Find the actual trace URI from all of the devices
		devices, err := filterDevices(ctx, &verb.DeviceFlags, client)
		if err != nil {
			return err
		}

		if len(devices) == 0 {
			return fmt.Errorf("Could not find matching device")
		}

		if len(devices) == 1 && strings.HasPrefix(traceURI, "port:") {
			target = func(opts *service.TraceOptions) {
				opts.Device = devices[0]
				opts.App = &service.TraceOptions_Uri{
					Uri: traceURI,
				}
			}
		} else if len(devices) == 1 && strings.HasPrefix(traceURI, "apk:") {
			data, err := ioutil.ReadFile(traceURI[4:])
			if err != nil {
				return log.Errf(ctx, err, "Failed to read APK at %s", traceURI[4:])
			}
			target = func(opts *service.TraceOptions) {
				opts.Device = devices[0]
				opts.App = &service.TraceOptions_UploadApplication{
					UploadApplication: data,
				}
			}
		} else if traceURI == "" {
			if len(devices) != 1 {
				return log.Errf(ctx, nil, "Found multiple matching devices, please specify the trace device")
			}
			target = func(opts *service.TraceOptions) {
				opts.Device = devices[0]
			}
		} else {
			type info struct {
				uri        string
				device     *path.Device
				deviceName string
				name       string
			}
			var found []info

			for _, dev := range devices {
				targets, err := client.FindTraceTargets(ctx, &service.FindTraceTargetsRequest{
					Device: dev,
					Uri:    traceURI,
				})
				if err != nil {
					continue
				}

				dd, err := client.Get(ctx, dev.Path(), nil)
				if err != nil {
					return err
				}
				d := dd.(*device.Instance)

				for _, target := range targets {
					name := target.Name
					switch {
					case target.FriendlyApplication != "":
						name = target.FriendlyApplication
					case target.FriendlyExecutable != "":
						name = target.FriendlyExecutable
					}

					found = append(found, info{
						uri:        target.Uri,
						deviceName: d.Name,
						device:     dev,
						name:       name,
					})
				}
			}

			if len(found) == 0 {
				return fmt.Errorf("Could not find %+v to trace on any device", traceURI)
			}

			if len(found) > 1 {
				sb := strings.Builder{}
				fmt.Fprintf(&sb, "Found %v candidates: \n", traceURI)
				for i, f := range found {
					if i == 0 || found[i-1].deviceName != f.deviceName {
						fmt.Fprintf(&sb, "  %v:\n", f.deviceName)
					}
					fmt.Fprintf(&sb, "    %v\n", f.uri)
				}
				return log.Errf(ctx, nil, "%v", sb.String())
			}

			fmt.Printf("Tracing %+v\n", found[0].uri)
			out = found[0].name + api.traceExt
			target = func(opts *service.TraceOptions) {
				opts.Device = found[0].device
				opts.App = &service.TraceOptions_Uri{
					Uri: found[0].uri,
				}
			}
		}
	}

	if verb.Out != "" {
		out = verb.Out
	}

	options := &service.TraceOptions{
		Type:                         api.traceType,
		Apis:                         api.apis,
		AdditionalCommandLineArgs:    verb.AdditionalArgs,
		Cwd:                          verb.WorkingDir,
		Environment:                  verb.Env,
		Duration:                     float32(verb.For.Seconds()),
		ObserveFrameFrequency:        uint32(verb.Observe.Frames),
		ObserveDrawFrequency:         uint32(verb.Observe.Draws),
		StartFrame:                   uint32(verb.Start.At.Frame),
		FramesToCapture:              uint32(verb.Capture.Frames),
		DisablePcs:                   verb.Disable.PCS,
		RecordErrorState:             verb.Record.Errors,
		DeferStart:                   verb.Start.Defer,
		NoBuffer:                     verb.No.Buffer,
		HideUnknownExtensions:        verb.Disable.Unknown.Extensions,
		RecordTraceTimes:             verb.Record.TraceTimes,
		ClearCache:                   verb.Clear.Cache,
		ServerLocalSavePath:          out,
		PipeName:                     verb.PipeName,
		DisableCoherentMemoryTracker: verb.Disable.CoherentMemoryTracker,
	}
	target(options)

	if api.traceType == service.TraceType_Perfetto {
		data, err := ioutil.ReadFile(verb.Perfetto)
		if err != nil {
			return log.Errf(ctx, err, "Failed to read Perfetto config")
		}
		options.PerfettoConfig = &perfetto_pb.TraceConfig{}
		if err := proto.UnmarshalText(string(data), options.PerfettoConfig); err != nil {
			return log.Errf(ctx, err, "Failed to parse Perfetto config")
		}
		dur := uint32(verb.For.Seconds() * 1000)
		if dur == 0 {
			dur = 10 * 60 * 1000
		}
		options.PerfettoConfig.DurationMs = proto.Uint32(dur)
		options.Duration = 0
	}

	handler, err := client.Trace(ctx)
	if err != nil {
		return err
	}
	defer handler.Dispose(ctx)

	defer app.AddInterruptHandler(func() {
		handler.Dispose(ctx)
	})()

	status, err := handler.Initialize(ctx, options)
	if err != nil {
		return err
	}
	log.I(ctx, "Trace Status %+v", status)

	handlerInstalled := options.Duration > 0

	return task.Retry(ctx, 0, time.Second*3, func(ctx context.Context) (retry bool, err error) {
		status, err = handler.Event(ctx, service.TraceEvent_Status)
		if err == io.EOF {
			return true, nil
		}
		if err != nil {
			log.I(ctx, "Error %+v", err)
			return true, err
		}
		if status == nil {
			return true, nil
		}

		if status.BytesCaptured > 0 {
			if !handlerInstalled {
				crash.Go(func() {
					reader := bufio.NewReader(os.Stdin)
					if options.DeferStart {
						println("Press enter to start capturing...")
						_, _ = reader.ReadString('\n')
						_, _ = handler.Event(ctx, service.TraceEvent_Begin)
					}
					println("Press enter to stop capturing...")
					_, _ = reader.ReadString('\n')
					handler.Event(ctx, service.TraceEvent_Stop)
				})
				handlerInstalled = true
			}
			log.I(ctx, "Captured bytes: %+v", status.BytesCaptured)
		}
		if status.Status == service.TraceStatus_Done {
			return true, nil
		}
		return false, nil
	})
}

type apiAndType struct {
	traceType service.TraceType
	apis      []string
	traceExt  string
}

func (verb *traceVerb) apiAndType() (apiAndType, error) {
	switch verb.API {
	case "vulkan":
		return apiAndType{
			service.TraceType_Graphics,
			[]string{"Vulkan"},
			".gfxtrace",
		}, nil
	case "gles":
		return apiAndType{
			service.TraceType_Graphics,
			[]string{"OpenGLES"},
			".gfxtrace",
		}, nil
	case "perfetto":
		return apiAndType{
			service.TraceType_Perfetto,
			[]string{},
			".perfetto",
		}, nil
	default:
		return apiAndType{}, fmt.Errorf("Unknown API '%s'", verb.API)
	}
}
<|endoftext|>


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

// This file implements access to gccgo-generated export data.

package main

import (
	"debug/elf"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"code.google.com/p/go.tools/go/gccgoimporter"
	"code.google.com/p/go.tools/go/importer"
	"code.google.com/p/go.tools/go/types"
)

func init() {
	// importer for default gccgo
	var inst gccgoimporter.GccgoInstallation
	inst.InitFromDriver("gccgo")
	register("gccgo", inst.GetImporter(nil))

	// importer for gccgo using condensed export format (experimental)
	register("gccgo-new", getNewImporter(append(inst.SearchPaths(), ".")))
}

// This function is an adjusted variant of gccgoimporter.GccgoInstallation.GetImporter.
func getNewImporter(searchpaths []string) types.Importer {
	return func(imports map[string]*types.Package, pkgpath string) (pkg *types.Package, err error) {
		if pkgpath == "unsafe" {
			return types.Unsafe, nil
		}

		fpath, err := findExportFile(searchpaths, pkgpath)
		if err != nil {
			return
		}

		reader, closer, err := openExportFile(fpath)
		if err != nil {
			return nil, err
		}
		defer closer.Close()

		// TODO(gri) At the moment we just read the entire file.
		// We should change importer.ImportData to take an io.Reader instead.
		data, err := ioutil.ReadAll(reader)
		if err != nil && err != io.EOF {
			return nil, err
		}

		return importer.ImportData(packages, data)
	}
}

// This function is an exact copy of gccgoimporter.findExportFile.
func findExportFile(searchpaths []string, pkgpath string) (string, error) {
	for _, spath := range searchpaths {
		pkgfullpath := filepath.Join(spath, pkgpath)
		pkgdir, name := filepath.Split(pkgfullpath)

		for _, filepath := range [...]string{
			pkgfullpath,
			pkgfullpath + ".gox",
			pkgdir + "lib" + name + ".so",
			pkgdir + "lib" + name + ".a",
			pkgfullpath + ".o",
		} {
			fi, err := os.Stat(filepath)
			if err == nil && !fi.IsDir() {
				return filepath, nil
			}
		}
	}

	return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":"))
}

// This function is an exact copy of gccgoimporter.openExportFile.
func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) {
	f, err := os.Open(fpath)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			f.Close()
		}
	}()
	closer = f

	var magic [4]byte
	_, err = f.ReadAt(magic[:], 0)
	if err != nil {
		return
	}

	if string(magic[:]) == "v1;\n" {
		// Raw export data.
		reader = f
		return
	}

	ef, err := elf.NewFile(f)
	if err != nil {
		return
	}

	sec := ef.Section(".go_export")
	if sec == nil {
		err = fmt.Errorf("%s: .go_export section not found", fpath)
		return
	}

	reader = sec.Open()
	return
}
<commit_msg>go.tools/cmd/godex: permit absolute paths for gccgo-generated package (export) files<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements access to gccgo-generated export data.

package main

import (
	"debug/elf"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"code.google.com/p/go.tools/go/gccgoimporter"
	"code.google.com/p/go.tools/go/importer"
	"code.google.com/p/go.tools/go/types"
)

func init() {
	incpaths := []string{"/"}

	// importer for default gccgo
	var inst gccgoimporter.GccgoInstallation
	inst.InitFromDriver("gccgo")
	register("gccgo", inst.GetImporter(incpaths))

	// importer for gccgo using condensed export format (experimental)
	register("gccgo-new", getNewImporter(append(append(incpaths, inst.SearchPaths()...), ".")))
}

// This function is an adjusted variant of gccgoimporter.GccgoInstallation.GetImporter.
func getNewImporter(searchpaths []string) types.Importer {
	return func(imports map[string]*types.Package, pkgpath string) (pkg *types.Package, err error) {
		if pkgpath == "unsafe" {
			return types.Unsafe, nil
		}

		fpath, err := findExportFile(searchpaths, pkgpath)
		if err != nil {
			return
		}

		reader, closer, err := openExportFile(fpath)
		if err != nil {
			return nil, err
		}
		defer closer.Close()

		// TODO(gri) At the moment we just read the entire file.
		// We should change importer.ImportData to take an io.Reader instead.
		data, err := ioutil.ReadAll(reader)
		if err != nil && err != io.EOF {
			return nil, err
		}

		return importer.ImportData(packages, data)
	}
}

// This function is an exact copy of gccgoimporter.findExportFile.
func findExportFile(searchpaths []string, pkgpath string) (string, error) {
	for _, spath := range searchpaths {
		pkgfullpath := filepath.Join(spath, pkgpath)
		pkgdir, name := filepath.Split(pkgfullpath)

		for _, filepath := range [...]string{
			pkgfullpath,
			pkgfullpath + ".gox",
			pkgdir + "lib" + name + ".so",
			pkgdir + "lib" + name + ".a",
			pkgfullpath + ".o",
		} {
			println("trying", filepath)
			fi, err := os.Stat(filepath)
			if err == nil && !fi.IsDir() {
				return filepath, nil
			}
		}
	}

	return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":"))
}

// This function is an exact copy of gccgoimporter.openExportFile.
func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) {
	f, err := os.Open(fpath)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			f.Close()
		}
	}()
	closer = f

	var magic [4]byte
	_, err = f.ReadAt(magic[:], 0)
	if err != nil {
		return
	}

	if string(magic[:]) == "v1;\n" {
		// Raw export data.
		reader = f
		return
	}

	ef, err := elf.NewFile(f)
	if err != nil {
		return
	}

	sec := ef.Section(".go_export")
	if sec == nil {
		err = fmt.Errorf("%s: .go_export section not found", fpath)
		return
	}

	reader = sec.Open()
	return
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"launchpad.net/gnuflag"

	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/juju"
	"launchpad.net/juju-core/juju/osenv"
	"launchpad.net/juju-core/names"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/state/api/params"
)

type DeployCommand struct {
	cmd.EnvCommandBase
	UnitCommandBase
	CharmName    string
	ServiceName  string
	Config       cmd.FileVar
	Constraints  constraints.Value
	Networks        string
	ExcludeNetworks string
	BumpRevision bool   // Remove this once the 1.16 support is dropped.
	RepoPath     string // defaults to JUJU_REPOSITORY
}

const deployDoc = `
<charm name> can be a charm URL, or an unambiguously condensed form of it;
assuming a current default series of "precise", the following forms will be
accepted.

For cs:precise/mysql
  mysql
  precise/mysql

For cs:~user/precise/mysql
  cs:~user/mysql

For local:precise/mysql
  local:mysql

In all cases, a versioned charm URL will be expanded as expected (for example,
mysql-33 becomes cs:precise/mysql-33).

<service name>, if omitted, will be derived from <charm name>.

Constraints can be specified when using deploy by specifying the --constraints
flag.  When used with deploy, service-specific constraints are set so that later
machines provisioned with add-unit will use the same constraints (unless changed
by set-constraints).

Charms can be deployed to a specific machine using the --to argument.

Like constraints, service-specific network requirements can be
specified with --networks and --exclude-networks arguments, both can
take a comma-delimited list of provider-specific network names/labels.
These instruct juju to ensure to add all the networks specified with
--networks to all new machines deployed to host units of the service
and to ensure none of the networks in --exclude-networks are added to
the service's machines. Not supported on all providers.

Examples:
   juju deploy mysql --to 23       (Deploy to machine 23)
   juju deploy mysql --to 24/lxc/3 (Deploy to lxc container 3 on host machine 24)
   juju deploy mysql --to lxc:25   (Deploy to a new lxc container on host machine 25)

   juju deploy mysql -n 5 --constraints mem=8G (deploy 5 instances of mysql with at least 8 GB of RAM each)

   juju deploy mysql --networks=storage,mynet --exclude-networks=logging

See Also:
   juju help constraints
   juju help set-constraints
   juju help get-constraints
`

func (c *DeployCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "deploy",
		Args:    "<charm name> [<service name>]",
		Purpose: "deploy a new service",
		Doc:     deployDoc,
	}
}

func (c *DeployCommand) SetFlags(f *gnuflag.FlagSet) {
	c.EnvCommandBase.SetFlags(f)
	c.UnitCommandBase.SetFlags(f)
	f.IntVar(&c.NumUnits, "n", 1, "number of service units to deploy for principal charms")
	f.BoolVar(&c.BumpRevision, "u", false, "increment local charm directory revision (DEPRECATED)")
	f.BoolVar(&c.BumpRevision, "upgrade", false, "")
	f.Var(&c.Config, "config", "path to yaml-formatted service config")
	f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "set service constraints")
	f.StringVar(&c.Networks, "networks", "", "enable networks for service")
	f.StringVar(&c.ExcludeNetworks, "exclude-networks", "", "disable networks for service")
	f.StringVar(&c.RepoPath, "repository", os.Getenv(osenv.JujuRepositoryEnvKey), "local charm repository")
}

func (c *DeployCommand) Init(args []string) error {
	switch len(args) {
	case 2:
		if !names.IsService(args[1]) {
			return fmt.Errorf("invalid service name %q", args[1])
		}
		c.ServiceName = args[1]
		fallthrough
	case 1:
		if _, err := charm.InferURL(args[0], "fake"); err != nil {
			return fmt.Errorf("invalid charm name %q", args[0])
		}
		c.CharmName = args[0]
	case 0:
		return errors.New("no charm specified")
	default:
		return cmd.CheckEmpty(args[2:])
	}
	return c.UnitCommandBase.Init(args)
}

func (c *DeployCommand) Run(ctx *cmd.Context) error {
	client, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer client.Close()

	attrs, err := client.EnvironmentGet()
	if params.IsCodeNotImplemented(err) {
		logger.Infof("EnvironmentGet not supported by the API server, " +
			"falling back to 1.16 compatibility mode (direct DB access)")
		return c.run1dot16(ctx)
	}
	if err != nil {
		return err
	}
	conf, err := config.New(config.NoDefaults, attrs)
	if err != nil {
		return err
	}
	curl, err := charm.InferURL(c.CharmName, conf.DefaultSeries())
	if err != nil {
		return err
	}
	repo, err := charm.InferRepository(curl, ctx.AbsPath(c.RepoPath))
	if err != nil {
		return err
	}

	repo = config.SpecializeCharmRepo(repo, conf)

	curl, err = addCharmViaAPI(client, ctx, curl, repo)
	if err != nil {
		return err
	}

	if c.BumpRevision {
		ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.")
	}
	var includeNetworks []string
	var excludeNetworks []string
	haveNetworks := false
	if c.Networks != "" {
		includeNetworks = parseNetworks(c.Networks)
		haveNetworks = true
	}
	if c.ExcludeNetworks != "" {
		excludeNetworks = parseNetworks(c.ExcludeNetworks)
		haveNetworks = true
	}
	if haveNetworks {
		env, err := environs.New(conf)
		if err != nil {
			return err
		}
		if !env.SupportNetworks() {
			return errors.New("cannot use --networks/--exclude-networks: not supported by the environment")
		}
	}

	charmInfo, err := client.CharmInfo(curl.String())
	if err != nil {
		return err
	}

	numUnits := c.NumUnits
	if charmInfo.Meta.Subordinate {
		if !constraints.IsEmpty(&c.Constraints) {
			return errors.New("cannot use --constraints with subordinate service")
		}
		if numUnits == 1 && c.ToMachineSpec == "" {
			numUnits = 0
		} else {
			return errors.New("cannot use --num-units or --to with subordinate service")
		}
	}
	serviceName := c.ServiceName
	if serviceName == "" {
		serviceName = charmInfo.Meta.Name
	}

	var configYAML []byte
	if c.Config.Path != "" {
		configYAML, err = c.Config.Read(ctx)
		if err != nil {
			return err
		}
	}
	err = client.ServiceDeployWithNetworks(
		curl.String(),
		serviceName,
		numUnits,
		string(configYAML),
		c.Constraints,
		c.ToMachineSpec,
		includeNetworks,
		excludeNetworks,
	)
	if params.IsCodeNotImplemented(err) && haveNetworks {
		return errors.New("cannot use --networks/--exclude-networks: not supported by the API server")
	}
	return client.ServiceDeploy(
		curl.String(),
		serviceName,
		numUnits,
		string(configYAML),
		c.Constraints,
		c.ToMachineSpec)
}

// run1dot16 implements the deploy command in 1.16 compatibility mode,
// with direct state access. Remove this when support for 1.16 is
// dropped.
func (c *DeployCommand) run1dot16(ctx *cmd.Context) error {
	conn, err := juju.NewConnFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer conn.Close()
	conf, err := conn.State.EnvironConfig()
	if err != nil {
		return err
	}
	curl, err := charm.InferURL(c.CharmName, conf.DefaultSeries())
	if err != nil {
		return err
	}
	repo, err := charm.InferRepository(curl, ctx.AbsPath(c.RepoPath))
	if err != nil {
		return err
	}

	repo = config.SpecializeCharmRepo(repo, conf)

	// TODO(fwereade) it's annoying to roundtrip the bytes through the client
	// here, but it's the original behaviour and not convenient to change.
	// PutCharm will always be required in some form for local charms; and we
	// will need an EnsureStoreCharm method somewhere that gets the state.Charm
	// for use in the following checks.
	ch, err := conn.PutCharm(curl, repo, c.BumpRevision)
	if err != nil {
		return err
	}
	numUnits := c.NumUnits
	if ch.Meta().Subordinate {
		if !constraints.IsEmpty(&c.Constraints) {
			return errors.New("cannot use --constraints with subordinate service")
		}
		if numUnits == 1 && c.ToMachineSpec == "" {
			numUnits = 0
		} else {
			return errors.New("cannot use --num-units or --to with subordinate service")
		}
	}

	serviceName := c.ServiceName
	if serviceName == "" {
		serviceName = ch.Meta().Name
	}
	var settings charm.Settings
	if c.Config.Path != "" {
		configYAML, err := c.Config.Read(ctx)
		if err != nil {
			return err
		}
		settings, err = ch.Config().ParseSettingsYAML(configYAML, serviceName)
		if err != nil {
			return err
		}
	}
	_, err = juju.DeployService(conn.State,
		juju.DeployServiceParams{
			ServiceName:    serviceName,
			Charm:          ch,
			NumUnits:       numUnits,
			ConfigSettings: settings,
			Constraints:    c.Constraints,
			ToMachineSpec:  c.ToMachineSpec,
		})
	return err
}

// addCharmViaAPI calls the appropriate client API calls to add the
// given charm URL to state. Also displays the charm URL of the added
// charm on stdout.
func addCharmViaAPI(client *api.Client, ctx *cmd.Context, curl *charm.URL, repo charm.Repository) (*charm.URL, error) {
	if curl.Revision < 0 {
		latest, err := charm.Latest(repo, curl)
		if err != nil {
			return nil, err
		}
		curl = curl.WithRevision(latest)
	}
	switch curl.Schema {
	case "local":
		ch, err := repo.Get(curl)
		if err != nil {
			return nil, err
		}
		stateCurl, err := client.AddLocalCharm(curl, ch)
		if err != nil {
			return nil, err
		}
		curl = stateCurl
	case "cs":
		err := client.AddCharm(curl)
		if err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("unsupported charm URL schema: %q", curl.Schema)
	}
	ctx.Infof("Added charm %q to the environment.", curl)
	return curl, nil
}

// parseNetworks returns a list of networks by parsing the
// comma-delimited string value of --networks or --no-networks
// arguments.
func parseNetworks(networksValue string) (networks []string) {
	parts := strings.Split(networksValue, ",")
	for _, part := range parts {
		network := strings.TrimSpace(part)
		if network != "" {
			networks = append(networks, network)
		}
	}
	return networks
}
<commit_msg>Fixed call order<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"launchpad.net/gnuflag"

	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/juju"
	"launchpad.net/juju-core/juju/osenv"
	"launchpad.net/juju-core/names"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/state/api/params"
)

type DeployCommand struct {
	cmd.EnvCommandBase
	UnitCommandBase
	CharmName       string
	ServiceName     string
	Config          cmd.FileVar
	Constraints     constraints.Value
	Networks        string
	ExcludeNetworks string
	BumpRevision    bool   // Remove this once the 1.16 support is dropped.
	RepoPath        string // defaults to JUJU_REPOSITORY
}

const deployDoc = `
<charm name> can be a charm URL, or an unambiguously condensed form of it;
assuming a current default series of "precise", the following forms will be
accepted.

For cs:precise/mysql
  mysql
  precise/mysql

For cs:~user/precise/mysql
  cs:~user/mysql

For local:precise/mysql
  local:mysql

In all cases, a versioned charm URL will be expanded as expected (for example,
mysql-33 becomes cs:precise/mysql-33).

<service name>, if omitted, will be derived from <charm name>.

Constraints can be specified when using deploy by specifying the --constraints
flag.  When used with deploy, service-specific constraints are set so that later
machines provisioned with add-unit will use the same constraints (unless changed
by set-constraints).

Charms can be deployed to a specific machine using the --to argument.

Like constraints, service-specific network requirements can be
specified with --networks and --exclude-networks arguments, both can
take a comma-delimited list of provider-specific network names/labels.
These instruct juju to ensure to add all the networks specified with
--networks to all new machines deployed to host units of the service
and to ensure none of the networks in --exclude-networks are added to
the service's machines. Not supported on all providers.

Examples:
   juju deploy mysql --to 23       (Deploy to machine 23)
   juju deploy mysql --to 24/lxc/3 (Deploy to lxc container 3 on host machine 24)
   juju deploy mysql --to lxc:25   (Deploy to a new lxc container on host machine 25)

   juju deploy mysql -n 5 --constraints mem=8G (deploy 5 instances of mysql with at least 8 GB of RAM each)

   juju deploy mysql --networks=storage,mynet --exclude-networks=logging

See Also:
   juju help constraints
   juju help set-constraints
   juju help get-constraints
`

func (c *DeployCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "deploy",
		Args:    "<charm name> [<service name>]",
		Purpose: "deploy a new service",
		Doc:     deployDoc,
	}
}

func (c *DeployCommand) SetFlags(f *gnuflag.FlagSet) {
	c.EnvCommandBase.SetFlags(f)
	c.UnitCommandBase.SetFlags(f)
	f.IntVar(&c.NumUnits, "n", 1, "number of service units to deploy for principal charms")
	f.BoolVar(&c.BumpRevision, "u", false, "increment local charm directory revision (DEPRECATED)")
	f.BoolVar(&c.BumpRevision, "upgrade", false, "")
	f.Var(&c.Config, "config", "path to yaml-formatted service config")
	f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "set service constraints")
	f.StringVar(&c.Networks, "networks", "", "enable networks for service")
	f.StringVar(&c.ExcludeNetworks, "exclude-networks", "", "disable networks for service")
	f.StringVar(&c.RepoPath, "repository", os.Getenv(osenv.JujuRepositoryEnvKey), "local charm repository")
}

func (c *DeployCommand) Init(args []string) error {
	switch len(args) {
	case 2:
		if !names.IsService(args[1]) {
			return fmt.Errorf("invalid service name %q", args[1])
		}
		c.ServiceName = args[1]
		fallthrough
	case 1:
		if _, err := charm.InferURL(args[0], "fake"); err != nil {
			return fmt.Errorf("invalid charm name %q", args[0])
		}
		c.CharmName = args[0]
	case 0:
		return errors.New("no charm specified")
	default:
		return cmd.CheckEmpty(args[2:])
	}
	return c.UnitCommandBase.Init(args)
}

func (c *DeployCommand) Run(ctx *cmd.Context) error {
	client, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer client.Close()

	attrs, err := client.EnvironmentGet()
	if params.IsCodeNotImplemented(err) {
		logger.Infof("EnvironmentGet not supported by the API server, " +
			"falling back to 1.16 compatibility mode (direct DB access)")
		return c.run1dot16(ctx)
	}
	if err != nil {
		return err
	}
	conf, err := config.New(config.NoDefaults, attrs)
	if err != nil {
		return err
	}
	curl, err := charm.InferURL(c.CharmName, conf.DefaultSeries())
	if err != nil {
		return err
	}
	repo, err := charm.InferRepository(curl, ctx.AbsPath(c.RepoPath))
	if err != nil {
		return err
	}

	repo = config.SpecializeCharmRepo(repo, conf)

	curl, err = addCharmViaAPI(client, ctx, curl, repo)
	if err != nil {
		return err
	}

	if c.BumpRevision {
		ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.")
	}
	var includeNetworks []string
	var excludeNetworks []string
	haveNetworks := false
	if c.Networks != "" {
		includeNetworks = parseNetworks(c.Networks)
		haveNetworks = true
	}
	if c.ExcludeNetworks != "" {
		excludeNetworks = parseNetworks(c.ExcludeNetworks)
		haveNetworks = true
	}
	if haveNetworks {
		env, err := environs.New(conf)
		if err != nil {
			return err
		}
		if !env.SupportNetworks() {
			return errors.New("cannot use --networks/--exclude-networks: not supported by the environment")
		}
	}

	charmInfo, err := client.CharmInfo(curl.String())
	if err != nil {
		return err
	}

	numUnits := c.NumUnits
	if charmInfo.Meta.Subordinate {
		if !constraints.IsEmpty(&c.Constraints) {
			return errors.New("cannot use --constraints with subordinate service")
		}
		if numUnits == 1 && c.ToMachineSpec == "" {
			numUnits = 0
		} else {
			return errors.New("cannot use --num-units or --to with subordinate service")
		}
	}
	serviceName := c.ServiceName
	if serviceName == "" {
		serviceName = charmInfo.Meta.Name
	}

	var configYAML []byte
	if c.Config.Path != "" {
		configYAML, err = c.Config.Read(ctx)
		if err != nil {
			return err
		}
	}
	err = client.ServiceDeployWithNetworks(
		curl.String(),
		serviceName,
		numUnits,
		string(configYAML),
		c.Constraints,
		c.ToMachineSpec,
		includeNetworks,
		excludeNetworks,
	)
	if params.IsCodeNotImplemented(err) {
		if haveNetworks {
			return errors.New("cannot use --networks/--exclude-networks: not supported by the API server")
		}
		err = client.ServiceDeploy(
			curl.String(),
			serviceName,
			numUnits,
			string(configYAML),
			c.Constraints,
			c.ToMachineSpec)
	}
	return err
}

// run1dot16 implements the deploy command in 1.16 compatibility mode,
// with direct state access. Remove this when support for 1.16 is
// dropped.
func (c *DeployCommand) run1dot16(ctx *cmd.Context) error {
	conn, err := juju.NewConnFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer conn.Close()
	conf, err := conn.State.EnvironConfig()
	if err != nil {
		return err
	}
	curl, err := charm.InferURL(c.CharmName, conf.DefaultSeries())
	if err != nil {
		return err
	}
	repo, err := charm.InferRepository(curl, ctx.AbsPath(c.RepoPath))
	if err != nil {
		return err
	}

	repo = config.SpecializeCharmRepo(repo, conf)

	// TODO(fwereade) it's annoying to roundtrip the bytes through the client
	// here, but it's the original behaviour and not convenient to change.
	// PutCharm will always be required in some form for local charms; and we
	// will need an EnsureStoreCharm method somewhere that gets the state.Charm
	// for use in the following checks.
	ch, err := conn.PutCharm(curl, repo, c.BumpRevision)
	if err != nil {
		return err
	}
	numUnits := c.NumUnits
	if ch.Meta().Subordinate {
		if !constraints.IsEmpty(&c.Constraints) {
			return errors.New("cannot use --constraints with subordinate service")
		}
		if numUnits == 1 && c.ToMachineSpec == "" {
			numUnits = 0
		} else {
			return errors.New("cannot use --num-units or --to with subordinate service")
		}
	}

	serviceName := c.ServiceName
	if serviceName == "" {
		serviceName = ch.Meta().Name
	}
	var settings charm.Settings
	if c.Config.Path != "" {
		configYAML, err := c.Config.Read(ctx)
		if err != nil {
			return err
		}
		settings, err = ch.Config().ParseSettingsYAML(configYAML, serviceName)
		if err != nil {
			return err
		}
	}
	_, err = juju.DeployService(conn.State,
		juju.DeployServiceParams{
			ServiceName:    serviceName,
			Charm:          ch,
			NumUnits:       numUnits,
			ConfigSettings: settings,
			Constraints:    c.Constraints,
			ToMachineSpec:  c.ToMachineSpec,
		})
	return err
}

// addCharmViaAPI calls the appropriate client API calls to add the
// given charm URL to state. Also displays the charm URL of the added
// charm on stdout.
func addCharmViaAPI(client *api.Client, ctx *cmd.Context, curl *charm.URL, repo charm.Repository) (*charm.URL, error) {
	if curl.Revision < 0 {
		latest, err := charm.Latest(repo, curl)
		if err != nil {
			return nil, err
		}
		curl = curl.WithRevision(latest)
	}
	switch curl.Schema {
	case "local":
		ch, err := repo.Get(curl)
		if err != nil {
			return nil, err
		}
		stateCurl, err := client.AddLocalCharm(curl, ch)
		if err != nil {
			return nil, err
		}
		curl = stateCurl
	case "cs":
		err := client.AddCharm(curl)
		if err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("unsupported charm URL schema: %q", curl.Schema)
	}
	ctx.Infof("Added charm %q to the environment.", curl)
	return curl, nil
}

// parseNetworks returns a list of networks by parsing the
// comma-delimited string value of --networks or --no-networks
// arguments.
func parseNetworks(networksValue string) (networks []string) {
	parts := strings.Split(networksValue, ",")
	for _, part := range parts {
		network := strings.TrimSpace(part)
		if network != "" {
			networks = append(networks, network)
		}
	}
	return networks
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"fmt"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	kgit "k8s.io/release/pkg/git"
	"k8s.io/release/pkg/util"
)

type ffOptions struct {
	branch         string
	masterRef      string
	nonInteractive bool
}

var ffOpts = &ffOptions{}

// ffCmd represents the base command when called without any subcommands
var ffCmd = &cobra.Command{
	Use:   "ff --branch <release-branch> [--ref <master-ref>] [--nomock] [--cleanup]",
	Short: "Fast forward a Kubernetes release branch",
	Long: fmt.Sprintf(`ff fast forwards a branch to a specified git object (defaults to %s).

krel ff pre-checks that the local branch to be forwarded is an actual
'release-x.y' branch and that the branch exists remotely. If that is not the
case, krel ff will fail.

After that preflight-check, the release branch will be checked out and krel
verifies that the latest merge base tag is the same for the master and the
release branch. This means that only the latest release branch can be fast
forwarded.

krel merges the provided ref into the release branch and asks for a final
confirmation if the push should really happen. The push will only be executed
as real push if the '--nomock' flag is specified.
`, kgit.Remotify(kgit.Master)),
	Example:       "krel ff --branch release-1.17 --ref origin/master --cleanup",
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runFf(ffOpts, rootOpts)
	},
}

const pushUpstreamQuestion = `Are you ready to push the local branch fast-forward changes upstream?
Please only answer after you have validated the changes.`

func init() {
	ffCmd.PersistentFlags().StringVar(&ffOpts.branch, "branch", "", "branch")
	ffCmd.PersistentFlags().StringVar(&ffOpts.masterRef, "ref", kgit.Remotify(kgit.Master), "ref on master")

	rootCmd.AddCommand(ffCmd)
}

func runFf(opts *ffOptions, rootOpts *rootOptions) error {
	branch := opts.branch
	if branch == "" {
		return errors.New("please specify valid release branch")
	}

	logrus.Infof("Preparing to fast-forward %s onto the %s branch", opts.masterRef, branch)
	repo, err := kgit.CloneOrOpenDefaultGitHubRepoSSH(rootOpts.repoPath)
	if err != nil {
		return err
	}

	if !rootOpts.nomock {
		logrus.Info("Using dry mode, which does not modify any remote content")
		repo.SetDry()
	}

	logrus.Infof("Checking if %q is a release branch", branch)
	if isReleaseBranch := kgit.IsReleaseBranch(branch); !isReleaseBranch {
		return errors.Errorf("%s is not a release branch", branch)
	}

	logrus.Info("Checking if branch is available on the default remote")
	if err := repo.HasRemoteBranch(branch); err != nil {
		return err
	}

	// Restore the currently checked out branch afterwards
	currentBranch, err := repo.CurrentBranch()
	if err != nil {
		return errors.Wrap(err, "unable to retrieve current branch")
	}
	defer func() {
		if err := repo.Checkout(currentBranch); err != nil {
			logrus.Errorf("unable to restore branch %s: %v", currentBranch, err)
		}
	}()
	logrus.Info("Checking out release branch")
	if err := repo.Checkout(branch); err != nil {
		return errors.Wrapf(err, "checking out branch %s", branch)
	}

	if rootOpts.cleanup {
		defer repo.Cleanup() // nolint: errcheck
	}

	logrus.Infof("Finding merge base between %q and %q", kgit.Master, branch)
	mergeBase, err := repo.MergeBase(kgit.Master, branch)
	if err != nil {
		return err
	}

	// Verify the tags
	masterTag, err := repo.Describe(
		kgit.NewDescribeOptions().
			WithRevision(kgit.Remotify(kgit.Master)).
			WithAbbrev(0).
			WithTags(),
	)

	if err != nil {
		return err
	}
	mergeBaseTag, err := repo.Describe(
		kgit.NewDescribeOptions().
			WithRevision(mergeBase).
			WithAbbrev(0).
			WithTags(),
	)
	if err != nil {
		return err
	}
	logrus.Infof("Merge base tag is: %s", mergeBaseTag)

	if masterTag != mergeBaseTag {
		return errors.Errorf(
			"unable to fast forward: tag %q does not match %q",
			masterTag, mergeBaseTag,
		)
	}
	logrus.Infof("Verified that the latest master tag is the same as the merge base tag")

	releaseRev, err := repo.Head()
	if err != nil {
		return err
	}
	logrus.Infof("Latest release branch revision is %s", releaseRev)

	logrus.Info("Merging master changes into release branch")
	if err := repo.Merge(opts.masterRef); err != nil {
		return err
	}

	headRev, err := repo.Head()
	if err != nil {
		return err
	}

	prepushMessage(repo.Dir(), branch, opts.masterRef, releaseRev, headRev)

	pushUpstream := false
	if opts.nonInteractive {
		pushUpstream = true
	} else {
		_, pushUpstream, err = util.Ask(pushUpstreamQuestion, "yes", 3)
		if err != nil {
			return err
		}
	}

	if pushUpstream {
		logrus.Infof("Pushing %s branch", branch)
		if err := repo.Push(branch); err != nil {
			return err
		}
	}

	return nil
}

func prepushMessage(gitRoot, branch, ref, releaseRev, headRev string) {
	fmt.Printf(`Go look around in %s to make sure things look okay before pushing…

Check for files left uncommitted using:

	git status -s

Validate the fast-forward commit using:

	git show

Validate the changes pulled in from master using:

	git log %s..%s

Once the branch fast-forward is complete, the diff will be available after push at:

	https://github.com/%s/%s/compare/%s...%s

`,
		gitRoot,
		kgit.Remotify(branch),
		ref,
		kgit.DefaultGithubOrg,
		kgit.DefaultGithubRepo,
		releaseRev[:11],
		headRev[:11],
	)
}
<commit_msg>krel/ff: dont restor the branch if the cleanup flag is set<commit_after>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"fmt"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	kgit "k8s.io/release/pkg/git"
	"k8s.io/release/pkg/util"
)

type ffOptions struct {
	branch         string
	masterRef      string
	nonInteractive bool
}

var ffOpts = &ffOptions{}

// ffCmd represents the base command when called without any subcommands
var ffCmd = &cobra.Command{
	Use:   "ff --branch <release-branch> [--ref <master-ref>] [--nomock] [--cleanup]",
	Short: "Fast forward a Kubernetes release branch",
	Long: fmt.Sprintf(`ff fast forwards a branch to a specified git object (defaults to %s).

krel ff pre-checks that the local branch to be forwarded is an actual
'release-x.y' branch and that the branch exists remotely. If that is not the
case, krel ff will fail.

After that preflight-check, the release branch will be checked out and krel
verifies that the latest merge base tag is the same for the master and the
release branch. This means that only the latest release branch can be fast
forwarded.

krel merges the provided ref into the release branch and asks for a final
confirmation if the push should really happen. The push will only be executed
as real push if the '--nomock' flag is specified.
`, kgit.Remotify(kgit.Master)),
	Example:       "krel ff --branch release-1.17 --ref origin/master --cleanup",
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runFf(ffOpts, rootOpts)
	},
}

const pushUpstreamQuestion = `Are you ready to push the local branch fast-forward changes upstream?
Please only answer after you have validated the changes.`

func init() {
	ffCmd.PersistentFlags().StringVar(&ffOpts.branch, "branch", "", "branch")
	ffCmd.PersistentFlags().StringVar(&ffOpts.masterRef, "ref", kgit.Remotify(kgit.Master), "ref on master")

	rootCmd.AddCommand(ffCmd)
}

func runFf(opts *ffOptions, rootOpts *rootOptions) error {
	branch := opts.branch
	if branch == "" {
		return errors.New("please specify valid release branch")
	}

	logrus.Infof("Preparing to fast-forward %s onto the %s branch", opts.masterRef, branch)
	repo, err := kgit.CloneOrOpenDefaultGitHubRepoSSH(rootOpts.repoPath)
	if err != nil {
		return err
	}

	if !rootOpts.nomock {
		logrus.Info("Using dry mode, which does not modify any remote content")
		repo.SetDry()
	}

	logrus.Infof("Checking if %q is a release branch", branch)
	if isReleaseBranch := kgit.IsReleaseBranch(branch); !isReleaseBranch {
		return errors.Errorf("%s is not a release branch", branch)
	}

	logrus.Info("Checking if branch is available on the default remote")
	if err := repo.HasRemoteBranch(branch); err != nil {
		return err
	}

	if rootOpts.cleanup {
		defer repo.Cleanup() // nolint: errcheck
	} else {
		// Restore the currently checked out branch afterwards
		currentBranch, err := repo.CurrentBranch()
		if err != nil {
			return errors.Wrap(err, "unable to retrieve current branch")
		}
		defer func() {
			if err := repo.Checkout(currentBranch); err != nil {
				logrus.Errorf("unable to restore branch %s: %v", currentBranch, err)
			}
		}()
	}

	logrus.Info("Checking out release branch")
	if err := repo.Checkout(branch); err != nil {
		return errors.Wrapf(err, "checking out branch %s", branch)
	}

	logrus.Infof("Finding merge base between %q and %q", kgit.Master, branch)
	mergeBase, err := repo.MergeBase(kgit.Master, branch)
	if err != nil {
		return err
	}

	// Verify the tags
	masterTag, err := repo.Describe(
		kgit.NewDescribeOptions().
			WithRevision(kgit.Remotify(kgit.Master)).
			WithAbbrev(0).
			WithTags(),
	)

	if err != nil {
		return err
	}
	mergeBaseTag, err := repo.Describe(
		kgit.NewDescribeOptions().
			WithRevision(mergeBase).
			WithAbbrev(0).
			WithTags(),
	)
	if err != nil {
		return err
	}
	logrus.Infof("Merge base tag is: %s", mergeBaseTag)

	if masterTag != mergeBaseTag {
		return errors.Errorf(
			"unable to fast forward: tag %q does not match %q",
			masterTag, mergeBaseTag,
		)
	}
	logrus.Infof("Verified that the latest master tag is the same as the merge base tag")

	releaseRev, err := repo.Head()
	if err != nil {
		return err
	}
	logrus.Infof("Latest release branch revision is %s", releaseRev)

	logrus.Info("Merging master changes into release branch")
	if err := repo.Merge(opts.masterRef); err != nil {
		return err
	}

	headRev, err := repo.Head()
	if err != nil {
		return err
	}

	prepushMessage(repo.Dir(), branch, opts.masterRef, releaseRev, headRev)

	pushUpstream := false
	if opts.nonInteractive {
		pushUpstream = true
	} else {
		_, pushUpstream, err = util.Ask(pushUpstreamQuestion, "yes", 3)
		if err != nil {
			return err
		}
	}

	if pushUpstream {
		logrus.Infof("Pushing %s branch", branch)
		if err := repo.Push(branch); err != nil {
			return err
		}
	}

	return nil
}

func prepushMessage(gitRoot, branch, ref, releaseRev, headRev string) {
	fmt.Printf(`Go look around in %s to make sure things look okay before pushing…

Check for files left uncommitted using:

	git status -s

Validate the fast-forward commit using:

	git show

Validate the changes pulled in from master using:

	git log %s..%s

Once the branch fast-forward is complete, the diff will be available after push at:

	https://github.com/%s/%s/compare/%s...%s

`,
		gitRoot,
		kgit.Remotify(branch),
		ref,
		kgit.DefaultGithubOrg,
		kgit.DefaultGithubRepo,
		releaseRev[:11],
		headRev[:11],
	)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"

	"github.com/loadimpact/k6/stats/cloud"
	"github.com/loadimpact/k6/ui"
	"github.com/pkg/errors"
        "github.com/spf13/cobra"
)

// loginCloudCommand represents the 'login cloud' command
var loginCloudCommand = &cobra.Command{
        Use:   "cloud",
	Short: "Authenticate with Load Impact",
        Long: `Authenticate with Load Impact.

This will set the default token used when just "k6 run -o cloud" is passed.`,
        Example: `
  # Show the stored token.
  k6 login cloud -s

  # Store a token.
  k6 login cloud -t YOUR_TOKEN

  # Log in with an email/password.
  k6 login cloud`[1:],
        Args: cobra.NoArgs,
        RunE: func(cmd *cobra.Command, args []string) error {
                config, cdir, err := readDiskConfig()
                if err != nil {
                        return err
                }

                flags := cmd.Flags()
                show := getNullBool(flags, "show")
                token, err := flags.GetString("token")
                if err != nil {
                        return err
                }

                printToken := func(conf cloud.Config) {
                        fmt.Fprintf(stdout, "  token: %s\n", ui.ValueColor.Sprint(conf.Token))
                }
                conf := config.Collectors.Cloud

                switch {
                case show.Bool:
                        printToken(conf)
                        return nil
                case token != "":
                        conf.Token = token
                default:
                        printToken(conf)

                        form := ui.Form{
                                Fields: []ui.Field{
                                        ui.StringField{
                                                Key:   "Email",
                                                Label: "Email",
					},
                                        ui.StringField{
                                                Key:   "Password",
                                                Label: "Password",
                                        },
                                },
                        }
                        vals, err := form.Run(os.Stdin, stdout)
                        if err != nil {
                                return err
                        }
                        email := vals["Email"].(string)
                        password := vals["Password"].(string)

                        client := cloud.NewClient("", conf.Host, Version)
                        res, err := client.Login(email, password)
                        if err != nil {
                                return err
                        }

                        if res.APIToken == "" {
                                return errors.New("Your account has no API token, please generate one: \"https://app.loadimpact.com/account/token\".")
                        }

                        conf.Token = res.APIToken
                }

                config.Collectors.Cloud = conf
                if err := writeDiskConfig(cdir, config); err != nil {
                        return err
                }

                printToken(conf)
		return nil
	},
}

func init() {
	loginCmd.AddCommand(loginCloudCommand)
	loginCloudCommand.Flags().StringP("token", "t", "", "specify `token` to use")
	loginCloudCommand.Flags().BoolP("show", "s", false, "display saved token and exit")
}
<commit_msg>case `show` do nothing<commit_after>package cmd

import (
	"fmt"
	"os"

	"github.com/loadimpact/k6/stats/cloud"
	"github.com/loadimpact/k6/ui"
	"github.com/pkg/errors"
        "github.com/spf13/cobra"
)

// loginCloudCommand represents the 'login cloud' command
var loginCloudCommand = &cobra.Command{
        Use:   "cloud",
	Short: "Authenticate with Load Impact",
        Long: `Authenticate with Load Impact.

This will set the default token used when just "k6 run -o cloud" is passed.`,
        Example: `
  # Show the stored token.
  k6 login cloud -s

  # Store a token.
  k6 login cloud -t YOUR_TOKEN

  # Log in with an email/password.
  k6 login cloud`[1:],
        Args: cobra.NoArgs,
        RunE: func(cmd *cobra.Command, args []string) error {
                config, cdir, err := readDiskConfig()
                if err != nil {
                        return err
                }

                flags := cmd.Flags()
                show := getNullBool(flags, "show")
                token, err := flags.GetString("token")
                if err != nil {
                        return err
                }

                printToken := func(conf cloud.Config) {
                        fmt.Fprintf(stdout, "  token: %s\n", ui.ValueColor.Sprint(conf.Token))
                }
                conf := config.Collectors.Cloud

                switch {
                case show.Bool:
                case token != "":
                        conf.Token = token
                default:
                        printToken(conf)

                        form := ui.Form{
                                Fields: []ui.Field{
                                        ui.StringField{
                                                Key:   "Email",
                                                Label: "Email",
					},
                                        ui.StringField{
                                                Key:   "Password",
                                                Label: "Password",
                                        },
                                },
                        }
                        vals, err := form.Run(os.Stdin, stdout)
                        if err != nil {
                                return err
                        }
                        email := vals["Email"].(string)
                        password := vals["Password"].(string)

                        client := cloud.NewClient("", conf.Host, Version)
                        res, err := client.Login(email, password)
                        if err != nil {
                                return err
                        }

                        if res.APIToken == "" {
                                return errors.New("Your account has no API token, please generate one: \"https://app.loadimpact.com/account/token\".")
                        }

                        conf.Token = res.APIToken
                }

                config.Collectors.Cloud = conf
                if err := writeDiskConfig(cdir, config); err != nil {
                        return err
                }

                printToken(conf)
		return nil
	},
}

func init() {
	loginCmd.AddCommand(loginCloudCommand)
	loginCloudCommand.Flags().StringP("token", "t", "", "specify `token` to use")
	loginCloudCommand.Flags().BoolP("show", "s", false, "display saved token and exit")
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/ogier/pflag"
	"github.com/uli-go/xz/lzma"
)

const (
	cmdName = "golzma"
	lzmaExt = ".lzma"
)

var (
	uncompress = pflag.BoolP("decompress", "d", false, "decompresses files")
)

func compressedName(name string) (string, error) {
	if filepath.Ext(name) == lzmaExt {
		return "", fmt.Errorf(
			"%s already has %s extension -- unchanged",
			name, lzmaExt)
	}
	return name + lzmaExt, nil
}

func cleanup(r, w *os.File, ferr error) error {
	var cerr error
	if r != nil {
		if err := r.Close(); err != nil {
			cerr = err
		}
		if ferr == nil {
			err := os.Remove(r.Name())
			if cerr == nil && err != nil {
				cerr = err
			}
		}
	}
	if w != nil {
		if err := w.Close(); cerr == nil && err != nil {
			cerr = err
		}
		if ferr != nil {
			err := os.Remove(w.Name())
			if cerr == nil && err != nil {
				cerr = err
			}
		}
	}
	if ferr == nil && cerr != nil {
		ferr = cerr
	}
	return ferr
}

func compressFile(name string) (err error) {
	var r, w *os.File
	defer func() {
		err = cleanup(r, w, err)
	}()
	compName, err := compressedName(name)
	if err != nil {
		return
	}
	r, err = os.Open(name)
	if err != nil {
		return
	}
	w, err = os.Create(compName)
	if err != nil {
		return
	}
	bw := bufio.NewWriter(w)
	lw, err := lzma.NewWriter(bw)
	if err != nil {
		return
	}
	if _, err = io.Copy(lw, r); err != nil {
		return
	}
	if err = lw.Close(); err != nil {
		return
	}
	err = bw.Flush()
	return
}

func uncompressedName(name string) (uname string, err error) {
	ext := filepath.Ext(name)
	if ext != lzmaExt {
		return "", fmt.Errorf(
			"%s: file extension %s unknown -- ignored", name, ext)
	}
	return name[:len(name)-len(ext)], nil
}

func uncompressFile(name string) (err error) {
	var r, w *os.File
	defer func() {
		err = cleanup(r, w, err)
	}()
	uname, err := uncompressedName(name)
	if err != nil {
		return
	}
	r, err = os.Open(name)
	if err != nil {
		return
	}
	lr, err := lzma.NewReader(bufio.NewReader(r))
	if err != nil {
		return
	}
	w, err = os.Create(uname)
	if err != nil {
		return
	}
	_, err = io.Copy(w, lr)
	return
}

func main() {
	pflag.Parse()
	status := 0
	if *uncompress {
		// uncompress files
		for _, name := range pflag.Args() {
			if err := uncompressFile(name); err != nil {
				status = 1
				fmt.Fprintf(os.Stderr, "%s: %s\n", cmdName, err)
			}
		}
	} else {
		// compress files
		for _, name := range pflag.Args() {
			if err := compressFile(name); err != nil {
				status = 1
				fmt.Fprintf(os.Stderr, "%s: %s\n", cmdName, err)
			}
		}
	}
	os.Exit(status)
}
<commit_msg>lzmago: simplified handling of error messages<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"

	"github.com/ogier/pflag"
	"github.com/uli-go/xz/lzma"
)

const (
	cmdName = "lzmago"
	lzmaExt = ".lzma"
)

var (
	uncompress = pflag.BoolP("decompress", "d", false, "decompresses files")
)

func compressedName(name string) (string, error) {
	if filepath.Ext(name) == lzmaExt {
		return "", fmt.Errorf(
			"%s already has %s extension -- unchanged",
			name, lzmaExt)
	}
	return name + lzmaExt, nil
}

func cleanup(r, w *os.File, ferr error) error {
	var cerr error
	if r != nil {
		if err := r.Close(); err != nil {
			cerr = err
		}
		if ferr == nil {
			err := os.Remove(r.Name())
			if cerr == nil && err != nil {
				cerr = err
			}
		}
	}
	if w != nil {
		if err := w.Close(); cerr == nil && err != nil {
			cerr = err
		}
		if ferr != nil {
			err := os.Remove(w.Name())
			if cerr == nil && err != nil {
				cerr = err
			}
		}
	}
	if ferr == nil && cerr != nil {
		ferr = cerr
	}
	return ferr
}

func compressFile(name string) (err error) {
	var r, w *os.File
	defer func() {
		err = cleanup(r, w, err)
	}()
	compName, err := compressedName(name)
	if err != nil {
		return
	}
	r, err = os.Open(name)
	if err != nil {
		return
	}
	w, err = os.Create(compName)
	if err != nil {
		return
	}
	bw := bufio.NewWriter(w)
	lw, err := lzma.NewWriter(bw)
	if err != nil {
		return
	}
	if _, err = io.Copy(lw, r); err != nil {
		return
	}
	if err = lw.Close(); err != nil {
		return
	}
	err = bw.Flush()
	return
}

func uncompressedName(name string) (uname string, err error) {
	ext := filepath.Ext(name)
	if ext != lzmaExt {
		return "", fmt.Errorf(
			"%s: file extension %s unknown -- ignored", name, ext)
	}
	return name[:len(name)-len(ext)], nil
}

func uncompressFile(name string) (err error) {
	var r, w *os.File
	defer func() {
		err = cleanup(r, w, err)
	}()
	uname, err := uncompressedName(name)
	if err != nil {
		return
	}
	r, err = os.Open(name)
	if err != nil {
		return
	}
	lr, err := lzma.NewReader(bufio.NewReader(r))
	if err != nil {
		return
	}
	w, err = os.Create(uname)
	if err != nil {
		return
	}
	_, err = io.Copy(w, lr)
	return
}

func main() {
	log.SetPrefix(fmt.Sprintf("%s: ", cmdName))
	log.SetFlags(0)
	pflag.Parse()
	if len(pflag.Args()) == 0 {
		log.Print("For help use option -h")
		os.Exit(0)
	}
	if *uncompress {
		// uncompress files
		for _, name := range pflag.Args() {
			if err := uncompressFile(name); err != nil {
				log.Fatal(err)
			}
		}
	} else {
		// compress files
		for _, name := range pflag.Args() {
			if err := compressFile(name); err != nil {
				log.Fatal(err)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"runtime"
	"sort"
	"strconv"
	"time"

	"github.com/go-errors/errors"
	isatty "github.com/mattn/go-isatty"
	lua "github.com/yuin/gopher-lua"
	"github.com/zyedidia/micro/v2/internal/action"
	"github.com/zyedidia/micro/v2/internal/buffer"
	"github.com/zyedidia/micro/v2/internal/config"
	"github.com/zyedidia/micro/v2/internal/screen"
	"github.com/zyedidia/micro/v2/internal/shell"
	"github.com/zyedidia/micro/v2/internal/util"
	"github.com/zyedidia/tcell"
)

var (
	// Event channel
	events   chan tcell.Event
	autosave chan bool

	// Command line flags
	flagVersion   = flag.Bool("version", false, "Show the version number and information")
	flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
	flagOptions   = flag.Bool("options", false, "Show all option help")
	flagDebug     = flag.Bool("debug", false, "Enable debug mode (prints debug info to ./log.txt)")
	flagPlugin    = flag.String("plugin", "", "Plugin command")
	flagClean     = flag.Bool("clean", false, "Clean configuration directory")
	optionFlags   map[string]*string
)

func InitFlags() {
	flag.Usage = func() {
		fmt.Println("Usage: micro [OPTIONS] [FILE]...")
		fmt.Println("-clean")
		fmt.Println("    \tCleans the configuration directory")
		fmt.Println("-config-dir dir")
		fmt.Println("    \tSpecify a custom location for the configuration directory")
		fmt.Println("[FILE]:LINE:COL")
		fmt.Println("+LINE:COL")
		fmt.Println("    \tSpecify a line and column to start the cursor at when opening a buffer")
		fmt.Println("-options")
		fmt.Println("    \tShow all option help")
		fmt.Println("-debug")
		fmt.Println("    \tEnable debug mode (enables logging to ./log.txt)")
		fmt.Println("-version")
		fmt.Println("    \tShow the version number and information")

		fmt.Print("\nMicro's plugin's can be managed at the command line with the following commands.\n")
		fmt.Println("-plugin install [PLUGIN]...")
		fmt.Println("    \tInstall plugin(s)")
		fmt.Println("-plugin remove [PLUGIN]...")
		fmt.Println("    \tRemove plugin(s)")
		fmt.Println("-plugin update [PLUGIN]...")
		fmt.Println("    \tUpdate plugin(s) (if no argument is given, updates all plugins)")
		fmt.Println("-plugin search [PLUGIN]...")
		fmt.Println("    \tSearch for a plugin")
		fmt.Println("-plugin list")
		fmt.Println("    \tList installed plugins")
		fmt.Println("-plugin available")
		fmt.Println("    \tList available plugins")

		fmt.Print("\nMicro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the settings.json\nfile (see 'help options').\n\n")
		fmt.Println("-option value")
		fmt.Println("    \tSet `option` to `value` for this session")
		fmt.Println("    \tFor example: `micro -syntax off file.c`")
		fmt.Println("\nUse `micro -options` to see the full list of configuration options")
	}

	optionFlags = make(map[string]*string)

	for k, v := range config.DefaultAllSettings() {
		optionFlags[k] = flag.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'.", k, v))
	}

	flag.Parse()

	if *flagVersion {
		// If -version was passed
		fmt.Println("Version:", util.Version)
		fmt.Println("Commit hash:", util.CommitHash)
		fmt.Println("Compiled on", util.CompileDate)
		os.Exit(0)
	}

	if *flagOptions {
		// If -options was passed
		var keys []string
		m := config.DefaultAllSettings()
		for k := range m {
			keys = append(keys, k)
		}
		sort.Strings(keys)
		for _, k := range keys {
			v := m[k]
			fmt.Printf("-%s value\n", k)
			fmt.Printf("    \tDefault value: '%v'\n", v)
		}
		os.Exit(0)
	}

	if util.Debug == "OFF" && *flagDebug {
		util.Debug = "ON"
	}
}

// DoPluginFlags parses and executes any flags that require LoadAllPlugins (-plugin and -clean)
func DoPluginFlags() {
	if *flagClean || *flagPlugin != "" {
		config.LoadAllPlugins()

		if *flagPlugin != "" {
			args := flag.Args()

			config.PluginCommand(os.Stdout, *flagPlugin, args)
		} else if *flagClean {
			CleanConfig()
		}

		os.Exit(0)
	}
}

// LoadInput determines which files should be loaded into buffers
// based on the input stored in flag.Args()
func LoadInput() []*buffer.Buffer {
	// There are a number of ways micro should start given its input

	// 1. If it is given a files in flag.Args(), it should open those

	// 2. If there is no input file and the input is not a terminal, that means
	// something is being piped in and the stdin should be opened in an
	// empty buffer

	// 3. If there is no input file and the input is a terminal, an empty buffer
	// should be opened

	var filename string
	var input []byte
	var err error
	args := flag.Args()
	buffers := make([]*buffer.Buffer, 0, len(args))

	btype := buffer.BTDefault
	if !isatty.IsTerminal(os.Stdout.Fd()) {
		btype = buffer.BTStdout
	}

	files := make([]string, 0, len(args))
	flagStartPos := buffer.Loc{-1, -1}
	flagr := regexp.MustCompile(`^\+(\d+)(?::(\d+))?$`)
	for _, a := range args {
		match := flagr.FindStringSubmatch(a)
		if len(match) == 3 && match[2] != "" {
			line, err := strconv.Atoi(match[1])
			if err != nil {
				screen.TermMessage(err)
				continue
			}
			col, err := strconv.Atoi(match[2])
			if err != nil {
				screen.TermMessage(err)
				continue
			}
			flagStartPos = buffer.Loc{col, line - 1}
		} else if len(match) == 3 && match[2] == "" {
			line, err := strconv.Atoi(match[1])
			if err != nil {
				screen.TermMessage(err)
				continue
			}
			flagStartPos = buffer.Loc{0, line - 1}
		} else {
			files = append(files, a)
		}
	}

	if len(files) > 0 {
		// Option 1
		// We go through each file and load it
		for i := 0; i < len(files); i++ {
			buf, err := buffer.NewBufferFromFileAtLoc(files[i], btype, flagStartPos)
			if err != nil {
				screen.TermMessage(err)
				continue
			}
			// If the file didn't exist, input will be empty, and we'll open an empty buffer
			buffers = append(buffers, buf)
		}
	} else if !isatty.IsTerminal(os.Stdin.Fd()) {
		// Option 2
		// The input is not a terminal, so something is being piped in
		// and we should read from stdin
		input, err = ioutil.ReadAll(os.Stdin)
		if err != nil {
			screen.TermMessage("Error reading from stdin: ", err)
			input = []byte{}
		}
		buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos))
	} else {
		// Option 3, just open an empty buffer
		buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos))
	}

	return buffers
}

func main() {
	defer func() {
		if util.Stdout.Len() > 0 {
			fmt.Fprint(os.Stdout, util.Stdout.String())
		}
		os.Exit(0)
	}()

	// runtime.SetCPUProfileRate(400)
	// f, _ := os.Create("micro.prof")
	// pprof.StartCPUProfile(f)
	// defer pprof.StopCPUProfile()

	var err error

	InitFlags()

	InitLog()

	err = config.InitConfigDir(*flagConfigDir)
	if err != nil {
		screen.TermMessage(err)
	}

	config.InitRuntimeFiles()
	err = config.ReadSettings()
	if err != nil {
		screen.TermMessage(err)
	}
	config.InitGlobalSettings()

	// flag options
	for k, v := range optionFlags {
		if *v != "" {
			nativeValue, err := config.GetNativeValue(k, config.DefaultAllSettings()[k], *v)
			if err != nil {
				screen.TermMessage(err)
				continue
			}
			config.GlobalSettings[k] = nativeValue
		}
	}

	DoPluginFlags()

	screen.Init()

	defer func() {
		if err := recover(); err != nil {
			screen.Screen.Fini()
			fmt.Println("Micro encountered an error:", err)
			// backup all open buffers
			for _, b := range buffer.OpenBuffers {
				b.Backup(false)
			}
			// Print the stack trace too
			fmt.Print(errors.Wrap(err, 2).ErrorStack())
			os.Exit(1)
		}
	}()

	err = config.LoadAllPlugins()
	if err != nil {
		screen.TermMessage(err)
	}

	action.InitBindings()
	action.InitCommands()

	err = config.InitColorscheme()
	if err != nil {
		screen.TermMessage(err)
	}

	b := LoadInput()

	if len(b) == 0 {
		// No buffers to open
		screen.Screen.Fini()
		runtime.Goexit()
	}

	action.InitTabs(b)
	action.InitGlobals()

	err = config.RunPluginFn("init")
	if err != nil {
		screen.TermMessage(err)
	}

	events = make(chan tcell.Event)

	// Here is the event loop which runs in a separate thread
	go func() {
		for {
			screen.Lock()
			e := screen.Screen.PollEvent()
			screen.Unlock()
			if e != nil {
				events <- e
			}
		}
	}()

	// clear the drawchan so we don't redraw excessively
	// if someone requested a redraw before we started displaying
	for len(screen.DrawChan()) > 0 {
		<-screen.DrawChan()
	}

	// wait for initial resize event
	select {
	case event := <-events:
		action.Tabs.HandleEvent(event)
	case <-time.After(10 * time.Millisecond):
		// time out after 10ms
	}

	// Since this loop is very slow (waits for user input every time) it's
	// okay to be inefficient and run it via a function every time
	// We do this so we can recover from panics without crashing the editor
	for {
		DoEvent()
	}
}

// DoEvent runs the main action loop of the editor
func DoEvent() {
	var event tcell.Event

	// recover from errors without crashing the editor
	defer func() {
		if err := recover(); err != nil {
			if e, ok := err.(*lua.ApiError); ok {
				screen.TermMessage("Lua API error:", e)
			} else {
				screen.TermMessage("Micro encountered an error:", errors.Wrap(err, 2).ErrorStack(), "\nIf you can reproduce this error, please report it at https://github.com/zyedidia/micro/issues")
			}
		}
	}()
	// Display everything
	screen.Screen.Fill(' ', config.DefStyle)
	screen.Screen.HideCursor()
	action.Tabs.Display()
	for _, ep := range action.MainTab().Panes {
		ep.Display()
	}
	action.MainTab().Display()
	action.InfoBar.Display()
	screen.Screen.Show()

	// Check for new events
	select {
	case f := <-shell.Jobs:
		// If a new job has finished while running in the background we should execute the callback
		f.Function(f.Output, f.Args)
	case <-config.Autosave:
		for _, b := range buffer.OpenBuffers {
			b.Save()
		}
	case <-shell.CloseTerms:
	case event = <-events:
	case <-screen.DrawChan():
		for len(screen.DrawChan()) > 0 {
			<-screen.DrawChan()
		}
	}

	if action.InfoBar.HasPrompt {
		action.InfoBar.HandleEvent(event)
	} else {
		action.Tabs.HandleEvent(event)
	}
}
<commit_msg>More fixes for parsecursor<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"runtime"
	"sort"
	"strconv"
	"time"

	"github.com/go-errors/errors"
	isatty "github.com/mattn/go-isatty"
	lua "github.com/yuin/gopher-lua"
	"github.com/zyedidia/micro/v2/internal/action"
	"github.com/zyedidia/micro/v2/internal/buffer"
	"github.com/zyedidia/micro/v2/internal/config"
	"github.com/zyedidia/micro/v2/internal/screen"
	"github.com/zyedidia/micro/v2/internal/shell"
	"github.com/zyedidia/micro/v2/internal/util"
	"github.com/zyedidia/tcell"
)

var (
	// Event channel
	events   chan tcell.Event
	autosave chan bool

	// Command line flags
	flagVersion   = flag.Bool("version", false, "Show the version number and information")
	flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
	flagOptions   = flag.Bool("options", false, "Show all option help")
	flagDebug     = flag.Bool("debug", false, "Enable debug mode (prints debug info to ./log.txt)")
	flagPlugin    = flag.String("plugin", "", "Plugin command")
	flagClean     = flag.Bool("clean", false, "Clean configuration directory")
	optionFlags   map[string]*string
)

func InitFlags() {
	flag.Usage = func() {
		fmt.Println("Usage: micro [OPTIONS] [FILE]...")
		fmt.Println("-clean")
		fmt.Println("    \tCleans the configuration directory")
		fmt.Println("-config-dir dir")
		fmt.Println("    \tSpecify a custom location for the configuration directory")
		fmt.Println("[FILE]:LINE:COL (if the `parsecursor` option is enabled)")
		fmt.Println("+LINE:COL")
		fmt.Println("    \tSpecify a line and column to start the cursor at when opening a buffer")
		fmt.Println("-options")
		fmt.Println("    \tShow all option help")
		fmt.Println("-debug")
		fmt.Println("    \tEnable debug mode (enables logging to ./log.txt)")
		fmt.Println("-version")
		fmt.Println("    \tShow the version number and information")

		fmt.Print("\nMicro's plugin's can be managed at the command line with the following commands.\n")
		fmt.Println("-plugin install [PLUGIN]...")
		fmt.Println("    \tInstall plugin(s)")
		fmt.Println("-plugin remove [PLUGIN]...")
		fmt.Println("    \tRemove plugin(s)")
		fmt.Println("-plugin update [PLUGIN]...")
		fmt.Println("    \tUpdate plugin(s) (if no argument is given, updates all plugins)")
		fmt.Println("-plugin search [PLUGIN]...")
		fmt.Println("    \tSearch for a plugin")
		fmt.Println("-plugin list")
		fmt.Println("    \tList installed plugins")
		fmt.Println("-plugin available")
		fmt.Println("    \tList available plugins")

		fmt.Print("\nMicro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the settings.json\nfile (see 'help options').\n\n")
		fmt.Println("-option value")
		fmt.Println("    \tSet `option` to `value` for this session")
		fmt.Println("    \tFor example: `micro -syntax off file.c`")
		fmt.Println("\nUse `micro -options` to see the full list of configuration options")
	}

	optionFlags = make(map[string]*string)

	for k, v := range config.DefaultAllSettings() {
		optionFlags[k] = flag.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'.", k, v))
	}

	flag.Parse()

	if *flagVersion {
		// If -version was passed
		fmt.Println("Version:", util.Version)
		fmt.Println("Commit hash:", util.CommitHash)
		fmt.Println("Compiled on", util.CompileDate)
		os.Exit(0)
	}

	if *flagOptions {
		// If -options was passed
		var keys []string
		m := config.DefaultAllSettings()
		for k := range m {
			keys = append(keys, k)
		}
		sort.Strings(keys)
		for _, k := range keys {
			v := m[k]
			fmt.Printf("-%s value\n", k)
			fmt.Printf("    \tDefault value: '%v'\n", v)
		}
		os.Exit(0)
	}

	if util.Debug == "OFF" && *flagDebug {
		util.Debug = "ON"
	}
}

// DoPluginFlags parses and executes any flags that require LoadAllPlugins (-plugin and -clean)
func DoPluginFlags() {
	if *flagClean || *flagPlugin != "" {
		config.LoadAllPlugins()

		if *flagPlugin != "" {
			args := flag.Args()

			config.PluginCommand(os.Stdout, *flagPlugin, args)
		} else if *flagClean {
			CleanConfig()
		}

		os.Exit(0)
	}
}

// LoadInput determines which files should be loaded into buffers
// based on the input stored in flag.Args()
func LoadInput() []*buffer.Buffer {
	// There are a number of ways micro should start given its input

	// 1. If it is given a files in flag.Args(), it should open those

	// 2. If there is no input file and the input is not a terminal, that means
	// something is being piped in and the stdin should be opened in an
	// empty buffer

	// 3. If there is no input file and the input is a terminal, an empty buffer
	// should be opened

	var filename string
	var input []byte
	var err error
	args := flag.Args()
	buffers := make([]*buffer.Buffer, 0, len(args))

	btype := buffer.BTDefault
	if !isatty.IsTerminal(os.Stdout.Fd()) {
		btype = buffer.BTStdout
	}

	files := make([]string, 0, len(args))
	flagStartPos := buffer.Loc{-1, -1}
	flagr := regexp.MustCompile(`^\+(\d+)(?::(\d+))?$`)
	for _, a := range args {
		match := flagr.FindStringSubmatch(a)
		if len(match) == 3 && match[2] != "" {
			line, err := strconv.Atoi(match[1])
			if err != nil {
				screen.TermMessage(err)
				continue
			}
			col, err := strconv.Atoi(match[2])
			if err != nil {
				screen.TermMessage(err)
				continue
			}
			flagStartPos = buffer.Loc{col - 1, line - 1}
		} else if len(match) == 3 && match[2] == "" {
			line, err := strconv.Atoi(match[1])
			if err != nil {
				screen.TermMessage(err)
				continue
			}
			flagStartPos = buffer.Loc{0, line - 1}
		} else {
			files = append(files, a)
		}
	}

	if len(files) > 0 {
		// Option 1
		// We go through each file and load it
		for i := 0; i < len(files); i++ {
			buf, err := buffer.NewBufferFromFileAtLoc(files[i], btype, flagStartPos)
			if err != nil {
				screen.TermMessage(err)
				continue
			}
			// If the file didn't exist, input will be empty, and we'll open an empty buffer
			buffers = append(buffers, buf)
		}
	} else if !isatty.IsTerminal(os.Stdin.Fd()) {
		// Option 2
		// The input is not a terminal, so something is being piped in
		// and we should read from stdin
		input, err = ioutil.ReadAll(os.Stdin)
		if err != nil {
			screen.TermMessage("Error reading from stdin: ", err)
			input = []byte{}
		}
		buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos))
	} else {
		// Option 3, just open an empty buffer
		buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos))
	}

	return buffers
}

func main() {
	defer func() {
		if util.Stdout.Len() > 0 {
			fmt.Fprint(os.Stdout, util.Stdout.String())
		}
		os.Exit(0)
	}()

	// runtime.SetCPUProfileRate(400)
	// f, _ := os.Create("micro.prof")
	// pprof.StartCPUProfile(f)
	// defer pprof.StopCPUProfile()

	var err error

	InitFlags()

	InitLog()

	err = config.InitConfigDir(*flagConfigDir)
	if err != nil {
		screen.TermMessage(err)
	}

	config.InitRuntimeFiles()
	err = config.ReadSettings()
	if err != nil {
		screen.TermMessage(err)
	}
	config.InitGlobalSettings()

	// flag options
	for k, v := range optionFlags {
		if *v != "" {
			nativeValue, err := config.GetNativeValue(k, config.DefaultAllSettings()[k], *v)
			if err != nil {
				screen.TermMessage(err)
				continue
			}
			config.GlobalSettings[k] = nativeValue
		}
	}

	DoPluginFlags()

	screen.Init()

	defer func() {
		if err := recover(); err != nil {
			screen.Screen.Fini()
			fmt.Println("Micro encountered an error:", err)
			// backup all open buffers
			for _, b := range buffer.OpenBuffers {
				b.Backup(false)
			}
			// Print the stack trace too
			fmt.Print(errors.Wrap(err, 2).ErrorStack())
			os.Exit(1)
		}
	}()

	err = config.LoadAllPlugins()
	if err != nil {
		screen.TermMessage(err)
	}

	action.InitBindings()
	action.InitCommands()

	err = config.InitColorscheme()
	if err != nil {
		screen.TermMessage(err)
	}

	b := LoadInput()

	if len(b) == 0 {
		// No buffers to open
		screen.Screen.Fini()
		runtime.Goexit()
	}

	action.InitTabs(b)
	action.InitGlobals()

	err = config.RunPluginFn("init")
	if err != nil {
		screen.TermMessage(err)
	}

	events = make(chan tcell.Event)

	// Here is the event loop which runs in a separate thread
	go func() {
		for {
			screen.Lock()
			e := screen.Screen.PollEvent()
			screen.Unlock()
			if e != nil {
				events <- e
			}
		}
	}()

	// clear the drawchan so we don't redraw excessively
	// if someone requested a redraw before we started displaying
	for len(screen.DrawChan()) > 0 {
		<-screen.DrawChan()
	}

	// wait for initial resize event
	select {
	case event := <-events:
		action.Tabs.HandleEvent(event)
	case <-time.After(10 * time.Millisecond):
		// time out after 10ms
	}

	// Since this loop is very slow (waits for user input every time) it's
	// okay to be inefficient and run it via a function every time
	// We do this so we can recover from panics without crashing the editor
	for {
		DoEvent()
	}
}

// DoEvent runs the main action loop of the editor
func DoEvent() {
	var event tcell.Event

	// recover from errors without crashing the editor
	defer func() {
		if err := recover(); err != nil {
			if e, ok := err.(*lua.ApiError); ok {
				screen.TermMessage("Lua API error:", e)
			} else {
				screen.TermMessage("Micro encountered an error:", errors.Wrap(err, 2).ErrorStack(), "\nIf you can reproduce this error, please report it at https://github.com/zyedidia/micro/issues")
			}
		}
	}()
	// Display everything
	screen.Screen.Fill(' ', config.DefStyle)
	screen.Screen.HideCursor()
	action.Tabs.Display()
	for _, ep := range action.MainTab().Panes {
		ep.Display()
	}
	action.MainTab().Display()
	action.InfoBar.Display()
	screen.Screen.Show()

	// Check for new events
	select {
	case f := <-shell.Jobs:
		// If a new job has finished while running in the background we should execute the callback
		f.Function(f.Output, f.Args)
	case <-config.Autosave:
		for _, b := range buffer.OpenBuffers {
			b.Save()
		}
	case <-shell.CloseTerms:
	case event = <-events:
	case <-screen.DrawChan():
		for len(screen.DrawChan()) > 0 {
			<-screen.DrawChan()
		}
	}

	if action.InfoBar.HasPrompt {
		action.InfoBar.HandleEvent(event)
	} else {
		action.Tabs.HandleEvent(event)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"os"

	"github.com/layeh/gumble/gumble"
	"github.com/layeh/gumble/gumbleutil"
	"github.com/layeh/piepan"
)

type strFlagSlice []string

func (s *strFlagSlice) Set(str string) error {
	*s = append(*s, str)
	return nil
}

func (s *strFlagSlice) String() string {
	return fmt.Sprintf("%v", *s)
}

func main() {
	// Flags
	username := flag.String("username", "piepan-bot", "username of the bot")
	password := flag.String("password", "", "user password")
	server := flag.String("server", "localhost:64738", "address of the server")
	certificateFile := flag.String("certificate", "", "user certificate file (PEM)")
	keyFile := flag.String("key", "", "user certificate key file (PEM)")
	insecure := flag.Bool("insecure", false, "skip certificate checking")
	lock := flag.String("lock", "", "server certificate lock file")
	ffmpeg := flag.String("ffmpeg", "ffmpeg", "ffmpeg-capable executable for media streaming")
	var accessTokens strFlagSlice
	flag.Var(&accessTokens, "access-token", "server access token (can be defined multiple times)")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "piepan v0.8.0\n")
		fmt.Fprintf(os.Stderr, "usage: %s [options] [script files]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "an easy to use framework for writing Mumble bots using Lua\n")
		flag.PrintDefaults()
	}

	flag.Parse()

	// Configuration
	config := gumble.NewConfig()
	config.Username = *username
	config.Password = *password
	config.Address = *server
	config.Tokens = gumble.AccessTokens(accessTokens)

	client := gumble.NewClient(config)
	instance := piepan.New(client)
	instance.AudioCommand = *ffmpeg

	if *insecure {
		config.TLSConfig.InsecureSkipVerify = true
	}
	if *lock != "" {
		gumbleutil.CertificateLockFile(client, *lock)
	}
	if *certificateFile != "" {
		if *keyFile == "" {
			keyFile = certificateFile
		}
		certificate, err := tls.LoadX509KeyPair(*certificateFile, *keyFile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err)
			os.Exit(1)
		}
		config.TLSConfig.Certificates = append(config.TLSConfig.Certificates, certificate)
	}

	client.Attach(gumbleutil.AutoBitrate)

	// Load scripts
	for _, script := range flag.Args() {
		if err := instance.LoadFile(script); err != nil {
			fmt.Fprintf(os.Stderr, "%s: %s\n", script, err)
		}
	}

	keepAlive := make(chan bool)
	exitStatus := 0
	client.Attach(gumbleutil.Listener{
		Disconnect: func(e *gumble.DisconnectEvent) {
			if e.Type != gumble.DisconnectUser {
				exitStatus = int(e.Type) + 1
			}
			keepAlive <- true
		},
	})

	if err := client.Connect(); err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err)
		os.Exit(1)
	}

	<-keepAlive
	os.Exit(exitStatus)
}
<commit_msg>bump v0.8.1<commit_after>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"os"

	"github.com/layeh/gumble/gumble"
	"github.com/layeh/gumble/gumbleutil"
	"github.com/layeh/piepan"
)

type strFlagSlice []string

func (s *strFlagSlice) Set(str string) error {
	*s = append(*s, str)
	return nil
}

func (s *strFlagSlice) String() string {
	return fmt.Sprintf("%v", *s)
}

func main() {
	// Flags
	username := flag.String("username", "piepan-bot", "username of the bot")
	password := flag.String("password", "", "user password")
	server := flag.String("server", "localhost:64738", "address of the server")
	certificateFile := flag.String("certificate", "", "user certificate file (PEM)")
	keyFile := flag.String("key", "", "user certificate key file (PEM)")
	insecure := flag.Bool("insecure", false, "skip certificate checking")
	lock := flag.String("lock", "", "server certificate lock file")
	ffmpeg := flag.String("ffmpeg", "ffmpeg", "ffmpeg-capable executable for media streaming")
	var accessTokens strFlagSlice
	flag.Var(&accessTokens, "access-token", "server access token (can be defined multiple times)")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "piepan v0.8.1\n")
		fmt.Fprintf(os.Stderr, "usage: %s [options] [script files]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "an easy to use framework for writing Mumble bots using Lua\n")
		flag.PrintDefaults()
	}

	flag.Parse()

	// Configuration
	config := gumble.NewConfig()
	config.Username = *username
	config.Password = *password
	config.Address = *server
	config.Tokens = gumble.AccessTokens(accessTokens)

	client := gumble.NewClient(config)
	instance := piepan.New(client)
	instance.AudioCommand = *ffmpeg

	if *insecure {
		config.TLSConfig.InsecureSkipVerify = true
	}
	if *lock != "" {
		gumbleutil.CertificateLockFile(client, *lock)
	}
	if *certificateFile != "" {
		if *keyFile == "" {
			keyFile = certificateFile
		}
		certificate, err := tls.LoadX509KeyPair(*certificateFile, *keyFile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err)
			os.Exit(1)
		}
		config.TLSConfig.Certificates = append(config.TLSConfig.Certificates, certificate)
	}

	client.Attach(gumbleutil.AutoBitrate)

	// Load scripts
	for _, script := range flag.Args() {
		if err := instance.LoadFile(script); err != nil {
			fmt.Fprintf(os.Stderr, "%s: %s\n", script, err)
		}
	}

	keepAlive := make(chan bool)
	exitStatus := 0
	client.Attach(gumbleutil.Listener{
		Disconnect: func(e *gumble.DisconnectEvent) {
			if e.Type != gumble.DisconnectUser {
				exitStatus = int(e.Type) + 1
			}
			keepAlive <- true
		},
	})

	if err := client.Connect(); err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err)
		os.Exit(1)
	}

	<-keepAlive
	os.Exit(exitStatus)
}
<|endoftext|>


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

/*
Present displays slide presentations and articles. It runs a web server that
presents slide and article files from the current directory.

It may be run as a stand-alone command or an App Engine app.
Instructions for deployment to App Engine are in the README of the
code.google.com/p/go.tools repository.

Usage of present:
  -base="": base path for slide template and static resources
  -http="127.0.0.1:3999": host:port to listen on

Input files are named foo.extension, where "extension" defines the format of
the generated output. The supported formats are:
	.slide        // HTML5 slide presentation
	.article      // article format, such as a blog post

The present file format is documented by the present package:
http://godoc.org/code.google.com/p/go.tools/present
*/
package main
<commit_msg>go.tools/cmd/present: update doc<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Present displays slide presentations and articles. It runs a web server that
presents slide and article files from the current directory.

It may be run as a stand-alone command or an App Engine app.
Instructions for deployment to App Engine are in the README of the
code.google.com/p/go.tools repository.

Usage of present:
  -base="": base path for slide template and static resources
  -http="127.0.0.1:3999": HTTP service address (e.g., '127.0.0.1:3999')
  -nacl=false: use Native Client environment playground (prevents non-Go code execution)
  -orighost="": host component of web origin URL (e.g., 'localhost')
  -play=true: enable playground (permit execution of arbitrary user code)

The setup of the Go version of NaCl is documented at:
https://code.google.com/p/go-wiki/wiki/NativeClient

Input files are named foo.extension, where "extension" defines the format of
the generated output. The supported formats are:
	.slide        // HTML5 slide presentation
	.article      // article format, such as a blog post

The present file format is documented by the present package:
http://godoc.org/code.google.com/p/go.tools/present
*/
package main
<|endoftext|>