2013-02-18 23:35:43 +00:00
|
|
|
/*
|
Rename import paths from camlistore.org to perkeep.org.
Part of the project renaming, issue #981.
After this, users will need to mv their $GOPATH/src/camlistore.org to
$GOPATH/src/perkeep.org. Sorry.
This doesn't yet rename the tools like camlistored, camput, camget,
camtool, etc.
Also, this only moves the lru package to internal. More will move to
internal later.
Also, this doesn't yet remove the "/pkg/" directory. That'll likely
happen later.
This updates some docs, but not all.
devcam test now passes again, even with Go 1.10 (which requires vet
checks are clean too). So a bunch of vet tests are fixed in this CL
too, and a bunch of other broken tests are now fixed (introduced from
the past week of merging the CL backlog).
Change-Id: If580db1691b5b99f8ed6195070789b1f44877dd4
2018-01-01 22:41:41 +00:00
|
|
|
Copyright 2013 The Perkeep Authors.
|
2013-02-18 23:35:43 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2018-04-22 19:35:28 +00:00
|
|
|
// Package cmdmain contains the shared implementation for pk-get,
|
2018-04-21 23:02:04 +00:00
|
|
|
// pk-put, pk, and other Perkeep command-line tools.
|
Rename import paths from camlistore.org to perkeep.org.
Part of the project renaming, issue #981.
After this, users will need to mv their $GOPATH/src/camlistore.org to
$GOPATH/src/perkeep.org. Sorry.
This doesn't yet rename the tools like camlistored, camput, camget,
camtool, etc.
Also, this only moves the lru package to internal. More will move to
internal later.
Also, this doesn't yet remove the "/pkg/" directory. That'll likely
happen later.
This updates some docs, but not all.
devcam test now passes again, even with Go 1.10 (which requires vet
checks are clean too). So a bunch of vet tests are fixed in this CL
too, and a bunch of other broken tests are now fixed (introduced from
the past week of merging the CL backlog).
Change-Id: If580db1691b5b99f8ed6195070789b1f44877dd4
2018-01-01 22:41:41 +00:00
|
|
|
package cmdmain // import "perkeep.org/pkg/cmdmain"
|
2013-02-18 23:35:43 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"os"
|
2018-03-06 21:39:14 +00:00
|
|
|
"os/exec"
|
2013-02-26 14:26:18 +00:00
|
|
|
"path/filepath"
|
2018-01-04 02:59:29 +00:00
|
|
|
"sort"
|
2018-03-06 21:39:14 +00:00
|
|
|
"strings"
|
2014-02-18 08:31:10 +00:00
|
|
|
"sync"
|
2013-02-18 23:35:43 +00:00
|
|
|
|
Rename import paths from camlistore.org to perkeep.org.
Part of the project renaming, issue #981.
After this, users will need to mv their $GOPATH/src/camlistore.org to
$GOPATH/src/perkeep.org. Sorry.
This doesn't yet rename the tools like camlistored, camput, camget,
camtool, etc.
Also, this only moves the lru package to internal. More will move to
internal later.
Also, this doesn't yet remove the "/pkg/" directory. That'll likely
happen later.
This updates some docs, but not all.
devcam test now passes again, even with Go 1.10 (which requires vet
checks are clean too). So a bunch of vet tests are fixed in this CL
too, and a bunch of other broken tests are now fixed (introduced from
the past week of merging the CL backlog).
Change-Id: If580db1691b5b99f8ed6195070789b1f44877dd4
2018-01-01 22:41:41 +00:00
|
|
|
"perkeep.org/pkg/buildinfo"
|
2015-12-16 16:14:51 +00:00
|
|
|
|
|
|
|
"go4.org/legal"
|
2013-02-18 23:35:43 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
FlagVersion = flag.Bool("version", false, "show version")
|
|
|
|
FlagHelp = flag.Bool("help", false, "print usage")
|
|
|
|
FlagVerbose = flag.Bool("verbose", false, "extra debug logging")
|
2015-12-16 16:14:51 +00:00
|
|
|
FlagLegal = flag.Bool("legal", false, "show licenses")
|
2013-02-18 23:35:43 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ExtraFlagRegistration allows to add more flags from
|
|
|
|
// other packages (with AddFlags) when Main starts.
|
|
|
|
ExtraFlagRegistration = func() {}
|
2016-11-17 23:01:43 +00:00
|
|
|
// PostFlag runs code that needs to happen after flags were parsed, but
|
|
|
|
// before the subcommand is run.
|
|
|
|
PostFlag = func() {}
|
2014-02-28 04:47:15 +00:00
|
|
|
// PreExit runs after the subcommand, but before Main terminates
|
|
|
|
// with either success or the error from the subcommand.
|
2013-02-18 23:35:43 +00:00
|
|
|
PreExit = func() {}
|
2013-02-27 21:04:08 +00:00
|
|
|
// ExitWithFailure determines whether the command exits
|
|
|
|
// with a non-zero exit status.
|
|
|
|
ExitWithFailure bool
|
2013-02-18 23:35:43 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var ErrUsage = UsageError("invalid command")
|
|
|
|
|
|
|
|
type UsageError string
|
|
|
|
|
|
|
|
func (ue UsageError) Error() string {
|
|
|
|
return "Usage error: " + string(ue)
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
// mode name to actual subcommand mapping
|
|
|
|
modeCommand = make(map[string]CommandRunner)
|
|
|
|
modeFlags = make(map[string]*flag.FlagSet)
|
2014-02-21 18:59:34 +00:00
|
|
|
wantHelp = make(map[string]*bool)
|
2018-03-06 21:39:14 +00:00
|
|
|
// asNewCommand stores whether the mode should actually be run as a new
|
|
|
|
// independent command.
|
|
|
|
asNewCommand = make(map[string]bool)
|
2013-02-18 23:35:43 +00:00
|
|
|
|
|
|
|
// Indirections for replacement by tests
|
|
|
|
Stderr io.Writer = os.Stderr
|
|
|
|
Stdout io.Writer = os.Stdout
|
|
|
|
Stdin io.Reader = os.Stdin
|
|
|
|
|
|
|
|
Exit = realExit
|
|
|
|
// TODO: abstract out vfs operation. should never call os.Stat, os.Open, os.Create, etc.
|
|
|
|
// Only use fs.Stat, fs.Open, where vs is an interface type.
|
|
|
|
// TODO: switch from using the global flag FlagSet and use our own. right now
|
|
|
|
// running "go test -v" dumps the flag usage data to the global stderr.
|
2016-11-17 23:01:43 +00:00
|
|
|
|
|
|
|
logger = log.New(Stderr, "", log.LstdFlags)
|
2013-02-18 23:35:43 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func realExit(code int) {
|
|
|
|
os.Exit(code)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CommandRunner is the type that a command mode should implement.
|
|
|
|
type CommandRunner interface {
|
|
|
|
Usage()
|
|
|
|
RunCommand(args []string) error
|
|
|
|
}
|
|
|
|
|
2018-03-06 21:39:14 +00:00
|
|
|
// ExecRunner is the type that a command mode should implement when that mode
|
|
|
|
// just calls a new executable that will run as a new command.
|
|
|
|
type ExecRunner interface {
|
|
|
|
CommandRunner
|
|
|
|
LookPath() (string, error)
|
|
|
|
}
|
|
|
|
|
2018-05-02 20:34:43 +00:00
|
|
|
// Demoter is an interface that boring commands can implement to
|
|
|
|
// demote themselves in the tool listing, for boring or low-level
|
|
|
|
// subcommands. They only show up in --help mode.
|
|
|
|
type Demoter interface {
|
|
|
|
CommandRunner
|
|
|
|
Demote() bool
|
|
|
|
}
|
|
|
|
|
2013-02-18 23:35:43 +00:00
|
|
|
type exampler interface {
|
|
|
|
Examples() []string
|
|
|
|
}
|
|
|
|
|
2013-02-25 20:40:11 +00:00
|
|
|
type describer interface {
|
|
|
|
Describe() string
|
|
|
|
}
|
|
|
|
|
2018-05-02 20:34:43 +00:00
|
|
|
func demote(c CommandRunner) bool {
|
|
|
|
i, ok := c.(Demoter)
|
|
|
|
return ok && i.Demote()
|
|
|
|
}
|
|
|
|
|
2018-03-06 21:39:14 +00:00
|
|
|
// RegisterMode adds a mode to the list of modes for the main command.
|
2013-02-18 23:35:43 +00:00
|
|
|
// It is meant to be called in init() for each subcommand.
|
2018-03-06 21:39:14 +00:00
|
|
|
func RegisterMode(mode string, makeCmd func(Flags *flag.FlagSet) CommandRunner) {
|
2013-02-18 23:35:43 +00:00
|
|
|
if _, dup := modeCommand[mode]; dup {
|
|
|
|
log.Fatalf("duplicate command %q registered", mode)
|
|
|
|
}
|
|
|
|
flags := flag.NewFlagSet(mode+" options", flag.ContinueOnError)
|
|
|
|
flags.Usage = func() {}
|
2014-02-21 18:59:34 +00:00
|
|
|
|
|
|
|
var cmdHelp bool
|
|
|
|
flags.BoolVar(&cmdHelp, "help", false, "Help for this mode.")
|
|
|
|
wantHelp[mode] = &cmdHelp
|
2013-02-18 23:35:43 +00:00
|
|
|
modeFlags[mode] = flags
|
|
|
|
modeCommand[mode] = makeCmd(flags)
|
|
|
|
}
|
|
|
|
|
2018-03-06 21:39:14 +00:00
|
|
|
// RegisterCommand adds a mode to the list of modes for the main command, and
|
|
|
|
// also specifies that this mode is just another executable that runs as a new
|
|
|
|
// cmdmain command. The executable to run is determined by the LookPath implementation
|
|
|
|
// for this mode.
|
|
|
|
func RegisterCommand(mode string, makeCmd func(Flags *flag.FlagSet) CommandRunner) {
|
|
|
|
RegisterMode(mode, makeCmd)
|
|
|
|
asNewCommand[mode] = true
|
|
|
|
}
|
|
|
|
|
2013-02-18 23:35:43 +00:00
|
|
|
func hasFlags(flags *flag.FlagSet) bool {
|
|
|
|
any := false
|
|
|
|
flags.VisitAll(func(*flag.Flag) {
|
|
|
|
any = true
|
|
|
|
})
|
|
|
|
return any
|
|
|
|
}
|
|
|
|
|
|
|
|
func usage(msg string) {
|
2013-02-26 14:26:18 +00:00
|
|
|
cmdName := filepath.Base(os.Args[0])
|
2013-02-18 23:35:43 +00:00
|
|
|
if msg != "" {
|
2013-02-26 21:42:38 +00:00
|
|
|
Errorf("Error: %v\n", msg)
|
2013-02-18 23:35:43 +00:00
|
|
|
}
|
2018-05-02 20:34:43 +00:00
|
|
|
var modesQualifer string
|
|
|
|
if !*FlagHelp {
|
|
|
|
modesQualifer = " (use --help to see all modes)"
|
|
|
|
}
|
2013-02-26 21:42:38 +00:00
|
|
|
Errorf(`
|
2018-05-02 20:34:43 +00:00
|
|
|
Usage: `+cmdName+` [globalopts] <mode> [commandopts] [commandargs]
|
2013-02-18 23:35:43 +00:00
|
|
|
|
2018-05-02 20:34:43 +00:00
|
|
|
Modes:%s
|
2013-02-25 20:40:11 +00:00
|
|
|
|
2018-05-02 20:34:43 +00:00
|
|
|
`, modesQualifer)
|
2018-01-04 02:59:29 +00:00
|
|
|
var modes []string
|
2013-02-18 23:35:43 +00:00
|
|
|
for mode, cmd := range modeCommand {
|
2018-05-02 20:34:43 +00:00
|
|
|
if des, ok := cmd.(describer); ok && (*FlagHelp || !demote(cmd)) {
|
2018-01-04 02:59:29 +00:00
|
|
|
modes = append(modes, fmt.Sprintf(" %s: %s\n", mode, des.Describe()))
|
2013-02-25 20:40:11 +00:00
|
|
|
}
|
|
|
|
}
|
2018-01-04 02:59:29 +00:00
|
|
|
sort.Strings(modes)
|
|
|
|
for i := range modes {
|
|
|
|
Errorf("%s", modes[i])
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:42:38 +00:00
|
|
|
Errorf("\nExamples:\n")
|
2018-01-04 02:59:29 +00:00
|
|
|
modes = nil
|
2013-02-25 20:40:11 +00:00
|
|
|
for mode, cmd := range modeCommand {
|
2018-05-02 20:34:43 +00:00
|
|
|
if ex, ok := cmd.(exampler); ok && (*FlagHelp || !demote(cmd)) {
|
2018-01-04 02:59:29 +00:00
|
|
|
line := ""
|
2014-02-09 23:41:52 +00:00
|
|
|
exs := ex.Examples()
|
|
|
|
if len(exs) > 0 {
|
2018-01-04 02:59:29 +00:00
|
|
|
line = "\n"
|
2014-02-09 23:41:52 +00:00
|
|
|
}
|
|
|
|
for _, example := range exs {
|
2018-01-04 02:59:29 +00:00
|
|
|
line += fmt.Sprintf(" %s %s %s\n", cmdName, mode, example)
|
2013-02-18 23:35:43 +00:00
|
|
|
}
|
2018-01-04 02:59:29 +00:00
|
|
|
modes = append(modes, line)
|
2013-02-18 23:35:43 +00:00
|
|
|
}
|
|
|
|
}
|
2018-01-04 02:59:29 +00:00
|
|
|
sort.Strings(modes)
|
|
|
|
for i := range modes {
|
|
|
|
Errorf("%s", modes[i])
|
|
|
|
}
|
2013-02-18 23:35:43 +00:00
|
|
|
|
2013-02-26 21:42:38 +00:00
|
|
|
Errorf(`
|
2013-02-18 23:35:43 +00:00
|
|
|
For mode-specific help:
|
|
|
|
|
|
|
|
` + cmdName + ` <mode> -help
|
|
|
|
|
|
|
|
Global options:
|
|
|
|
`)
|
|
|
|
flag.PrintDefaults()
|
|
|
|
Exit(1)
|
|
|
|
}
|
|
|
|
|
2013-02-25 20:40:11 +00:00
|
|
|
func help(mode string) {
|
|
|
|
cmdName := os.Args[0]
|
|
|
|
// We can skip all the checks as they're done in Main
|
|
|
|
cmd := modeCommand[mode]
|
|
|
|
cmdFlags := modeFlags[mode]
|
2014-02-19 02:41:51 +00:00
|
|
|
cmdFlags.SetOutput(Stderr)
|
2013-02-25 20:40:11 +00:00
|
|
|
if des, ok := cmd.(describer); ok {
|
2013-02-26 21:42:38 +00:00
|
|
|
Errorf("%s\n", des.Describe())
|
2013-02-25 20:40:11 +00:00
|
|
|
}
|
2013-02-26 21:42:38 +00:00
|
|
|
Errorf("\n")
|
2013-02-25 20:40:11 +00:00
|
|
|
cmd.Usage()
|
|
|
|
if hasFlags(cmdFlags) {
|
|
|
|
cmdFlags.PrintDefaults()
|
|
|
|
}
|
|
|
|
if ex, ok := cmd.(exampler); ok {
|
2013-02-26 21:42:38 +00:00
|
|
|
Errorf("\nExamples:\n")
|
2013-02-25 20:40:11 +00:00
|
|
|
for _, example := range ex.Examples() {
|
2013-02-26 21:42:38 +00:00
|
|
|
Errorf(" %s %s %s\n", cmdName, mode, example)
|
2013-02-25 20:40:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-18 08:31:10 +00:00
|
|
|
// registerFlagOnce guards ExtraFlagRegistration. Tests may invoke
|
|
|
|
// Main multiple times, but duplicate flag registration is fatal.
|
|
|
|
var registerFlagOnce sync.Once
|
|
|
|
|
2014-02-19 02:41:51 +00:00
|
|
|
var setCommandLineOutput func(io.Writer) // or nil if before Go 1.2
|
|
|
|
|
2015-12-16 16:14:51 +00:00
|
|
|
// PrintLicenses prints all the licences registered by go4.org/legal for this program.
|
|
|
|
func PrintLicenses() {
|
|
|
|
for _, text := range legal.Licenses() {
|
|
|
|
fmt.Fprintln(Stderr, text)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-18 23:35:43 +00:00
|
|
|
// Main is meant to be the core of a command that has
|
2018-04-21 23:02:04 +00:00
|
|
|
// subcommands (modes), such as pk-put or pk.
|
2013-02-27 21:04:08 +00:00
|
|
|
func Main() {
|
2014-02-18 08:31:10 +00:00
|
|
|
registerFlagOnce.Do(ExtraFlagRegistration)
|
2014-02-19 02:41:51 +00:00
|
|
|
if setCommandLineOutput != nil {
|
|
|
|
setCommandLineOutput(Stderr)
|
|
|
|
}
|
2016-12-09 06:29:27 +00:00
|
|
|
flag.Usage = func() {
|
|
|
|
usage("")
|
|
|
|
}
|
2013-02-18 23:35:43 +00:00
|
|
|
flag.Parse()
|
2017-11-20 20:14:23 +00:00
|
|
|
flag.CommandLine.SetOutput(Stderr)
|
2016-11-17 23:01:43 +00:00
|
|
|
PostFlag()
|
2014-02-19 02:41:51 +00:00
|
|
|
|
2013-02-18 23:35:43 +00:00
|
|
|
args := flag.Args()
|
|
|
|
if *FlagVersion {
|
2018-05-02 20:34:43 +00:00
|
|
|
fmt.Fprintf(Stderr, "%s version: %s\n", os.Args[0], buildinfo.Summary())
|
2013-02-27 21:04:08 +00:00
|
|
|
return
|
2013-02-18 23:35:43 +00:00
|
|
|
}
|
|
|
|
if *FlagHelp {
|
|
|
|
usage("")
|
|
|
|
}
|
2015-12-16 16:14:51 +00:00
|
|
|
if *FlagLegal {
|
|
|
|
PrintLicenses()
|
2014-07-03 20:03:31 +00:00
|
|
|
return
|
|
|
|
}
|
2013-02-18 23:35:43 +00:00
|
|
|
if len(args) == 0 {
|
|
|
|
usage("No mode given.")
|
|
|
|
}
|
|
|
|
|
|
|
|
mode := args[0]
|
|
|
|
cmd, ok := modeCommand[mode]
|
|
|
|
if !ok {
|
|
|
|
usage(fmt.Sprintf("Unknown mode %q", mode))
|
|
|
|
}
|
|
|
|
|
2018-03-06 21:39:14 +00:00
|
|
|
if _, ok := asNewCommand[mode]; ok {
|
|
|
|
runAsNewCommand(cmd, mode)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2013-02-18 23:35:43 +00:00
|
|
|
cmdFlags := modeFlags[mode]
|
2014-02-19 02:41:51 +00:00
|
|
|
cmdFlags.SetOutput(Stderr)
|
2013-02-18 23:35:43 +00:00
|
|
|
err := cmdFlags.Parse(args[1:])
|
|
|
|
if err != nil {
|
2018-03-06 21:39:14 +00:00
|
|
|
// We want -h to behave as -help, but without having to define another flag for
|
|
|
|
// it, so we handle it here.
|
|
|
|
// TODO(mpl): maybe even remove -help and just let them both be handled here?
|
|
|
|
if err == flag.ErrHelp {
|
|
|
|
help(mode)
|
|
|
|
return
|
|
|
|
}
|
2013-02-18 23:35:43 +00:00
|
|
|
err = ErrUsage
|
|
|
|
} else {
|
2014-02-21 18:59:34 +00:00
|
|
|
if *wantHelp[mode] {
|
2013-02-25 20:40:11 +00:00
|
|
|
help(mode)
|
2013-02-27 21:04:08 +00:00
|
|
|
return
|
2013-02-25 20:40:11 +00:00
|
|
|
}
|
2013-02-18 23:35:43 +00:00
|
|
|
err = cmd.RunCommand(cmdFlags.Args())
|
|
|
|
}
|
|
|
|
if ue, isUsage := err.(UsageError); isUsage {
|
|
|
|
if isUsage {
|
2013-02-26 21:42:38 +00:00
|
|
|
Errorf("%s\n", ue)
|
2013-02-18 23:35:43 +00:00
|
|
|
}
|
|
|
|
cmd.Usage()
|
2013-02-26 21:42:38 +00:00
|
|
|
Errorf("\nGlobal options:\n")
|
2013-02-18 23:35:43 +00:00
|
|
|
flag.PrintDefaults()
|
|
|
|
|
|
|
|
if hasFlags(cmdFlags) {
|
2013-02-26 21:42:38 +00:00
|
|
|
Errorf("\nMode-specific options for mode %q:\n", mode)
|
2013-02-18 23:35:43 +00:00
|
|
|
cmdFlags.PrintDefaults()
|
|
|
|
}
|
|
|
|
Exit(1)
|
|
|
|
}
|
2014-02-28 04:47:15 +00:00
|
|
|
PreExit()
|
2013-02-27 21:04:08 +00:00
|
|
|
if err != nil {
|
|
|
|
if !ExitWithFailure {
|
|
|
|
// because it was already logged if ExitWithFailure
|
2014-11-03 21:15:02 +00:00
|
|
|
Errorf("Error: %v\n", err)
|
2013-02-27 21:04:08 +00:00
|
|
|
}
|
|
|
|
Exit(2)
|
|
|
|
}
|
2013-02-18 23:35:43 +00:00
|
|
|
}
|
2016-11-17 23:01:43 +00:00
|
|
|
|
2018-03-06 21:39:14 +00:00
|
|
|
// runAsNewCommand runs the executable specified by cmd's LookPath, which means
|
|
|
|
// cmd must implement the ExecRunner interface. The executable must be a binary of
|
|
|
|
// a program that runs Main.
|
|
|
|
func runAsNewCommand(cmd CommandRunner, mode string) {
|
|
|
|
execCmd, ok := cmd.(ExecRunner)
|
|
|
|
if !ok {
|
|
|
|
panic(fmt.Sprintf("%v does not implement ExecRunner", mode))
|
|
|
|
}
|
|
|
|
cmdPath, err := execCmd.LookPath()
|
|
|
|
if err != nil {
|
|
|
|
Errorf("Error: %v\n", err)
|
|
|
|
Exit(2)
|
|
|
|
}
|
|
|
|
allArgs := shiftFlags(mode)
|
|
|
|
if err := runExec(cmdPath, allArgs, newCopyEnv()); err != nil {
|
|
|
|
panic(fmt.Sprintf("running %v should have ended with an os.Exit, and not leave us with that error: %v", cmdPath, err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// shiftFlags prepends all the arguments (global flags) passed before the given
|
|
|
|
// mode to the list of arguments after that mode, and returns that list.
|
|
|
|
func shiftFlags(mode string) []string {
|
|
|
|
modePos := 0
|
|
|
|
for k, v := range os.Args {
|
|
|
|
if v == mode {
|
|
|
|
modePos = k
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
globalFlags := os.Args[1:modePos]
|
|
|
|
return append(globalFlags, os.Args[modePos+1:]...)
|
|
|
|
}
|
|
|
|
|
2016-11-17 23:01:43 +00:00
|
|
|
// Errorf prints to Stderr, regardless of FlagVerbose.
|
|
|
|
func Errorf(format string, args ...interface{}) {
|
|
|
|
fmt.Fprintf(Stderr, format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Printf prints to Stderr if FlagVerbose, and is silent otherwise.
|
|
|
|
func Printf(format string, args ...interface{}) {
|
|
|
|
if *FlagVerbose {
|
|
|
|
fmt.Fprintf(Stderr, format, args...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Logf logs to Stderr if FlagVerbose, and is silent otherwise.
|
|
|
|
func Logf(format string, v ...interface{}) {
|
|
|
|
if !*FlagVerbose {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
logger.Printf(format, v...)
|
|
|
|
}
|
2018-03-06 21:39:14 +00:00
|
|
|
|
|
|
|
// sysExec is set to syscall.Exec on platforms that support it.
|
|
|
|
var sysExec func(argv0 string, argv []string, envv []string) (err error)
|
|
|
|
|
|
|
|
// runExec execs bin. If the platform doesn't support exec, it runs it and waits
|
|
|
|
// for it to finish.
|
|
|
|
func runExec(bin string, args []string, e *env) error {
|
|
|
|
if sysExec != nil {
|
|
|
|
sysExec(bin, append([]string{filepath.Base(bin)}, args...), e.flat())
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd := exec.Command(bin, args...)
|
|
|
|
cmd.Env = e.flat()
|
|
|
|
cmd.Stdout = Stdout
|
|
|
|
cmd.Stderr = Stderr
|
|
|
|
return cmd.Run()
|
|
|
|
}
|
|
|
|
|
|
|
|
type env struct {
|
|
|
|
m map[string]string
|
|
|
|
order []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *env) set(k, v string) {
|
|
|
|
_, dup := e.m[k]
|
|
|
|
e.m[k] = v
|
|
|
|
if !dup {
|
|
|
|
e.order = append(e.order, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *env) flat() []string {
|
|
|
|
vv := make([]string, 0, len(e.order))
|
|
|
|
for _, k := range e.order {
|
|
|
|
if v, ok := e.m[k]; ok {
|
|
|
|
vv = append(vv, k+"="+v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return vv
|
|
|
|
}
|
|
|
|
|
|
|
|
func newCopyEnv() *env {
|
|
|
|
e := &env{make(map[string]string), nil}
|
|
|
|
for _, kv := range os.Environ() {
|
|
|
|
eq := strings.Index(kv, "=")
|
|
|
|
if eq > 0 {
|
|
|
|
e.set(kv[:eq], kv[eq+1:])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return e
|
|
|
|
}
|