mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-04-29 08:54:10 +00:00
v1.0.0
This commit is contained in:
parent
47d23e9972
commit
c7f7c08ead
711 changed files with 82154 additions and 2 deletions
122
main/commands/base/command.go
Normal file
122
main/commands/base/command.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// Package base defines shared basic pieces of the commands,
|
||||
// in particular logging and the Command structure.
|
||||
package base
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A Command is an implementation of a xray command
|
||||
// like xray run or xray version.
|
||||
type Command struct {
|
||||
// Run runs the command.
|
||||
// The args are the arguments after the command name.
|
||||
Run func(cmd *Command, args []string)
|
||||
|
||||
// UsageLine is the one-line usage message.
|
||||
// The words between "go" and the first flag or argument in the line are taken to be the command name.
|
||||
UsageLine string
|
||||
|
||||
// Short is the short description shown in the 'go help' output.
|
||||
Short string
|
||||
|
||||
// Long is the long message shown in the 'go help <this-command>' output.
|
||||
Long string
|
||||
|
||||
// Flag is a set of flags specific to this command.
|
||||
Flag flag.FlagSet
|
||||
|
||||
// CustomFlags indicates that the command will do its own
|
||||
// flag parsing.
|
||||
CustomFlags bool
|
||||
|
||||
// Commands lists the available commands and help topics.
|
||||
// The order here is the order in which they are printed by 'go help'.
|
||||
// Note that subcommands are in general best avoided.
|
||||
Commands []*Command
|
||||
}
|
||||
|
||||
// LongName returns the command's long name: all the words in the usage line between "go" and a flag or argument,
|
||||
func (c *Command) LongName() string {
|
||||
name := c.UsageLine
|
||||
if i := strings.Index(name, " ["); i >= 0 {
|
||||
name = name[:i]
|
||||
}
|
||||
if name == CommandEnv.Exec {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimPrefix(name, CommandEnv.Exec+" ")
|
||||
}
|
||||
|
||||
// Name returns the command's short name: the last word in the usage line before a flag or argument.
|
||||
func (c *Command) Name() string {
|
||||
name := c.LongName()
|
||||
if i := strings.LastIndex(name, " "); i >= 0 {
|
||||
name = name[i+1:]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Usage prints usage of the Command
|
||||
func (c *Command) Usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: %s\n", c.UsageLine)
|
||||
fmt.Fprintf(os.Stderr, "Run 'xray help %s' for details.\n", c.LongName())
|
||||
SetExitStatus(2)
|
||||
Exit()
|
||||
}
|
||||
|
||||
// Runnable reports whether the command can be run; otherwise
|
||||
// it is a documentation pseudo-command such as importpath.
|
||||
func (c *Command) Runnable() bool {
|
||||
return c.Run != nil
|
||||
}
|
||||
|
||||
// Exit exits with code set with SetExitStatus()
|
||||
func Exit() {
|
||||
os.Exit(exitStatus)
|
||||
}
|
||||
|
||||
// Fatalf logs error and exit with code 1
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
Errorf(format, args...)
|
||||
Exit()
|
||||
}
|
||||
|
||||
// Errorf logs error and set exit status to 1, but not exit
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, format, args...)
|
||||
fmt.Fprintln(os.Stderr)
|
||||
SetExitStatus(1)
|
||||
}
|
||||
|
||||
// ExitIfErrors exits if current status is not zero
|
||||
func ExitIfErrors() {
|
||||
if exitStatus != 0 {
|
||||
Exit()
|
||||
}
|
||||
}
|
||||
|
||||
var exitStatus = 0
|
||||
var exitMu sync.Mutex
|
||||
|
||||
// SetExitStatus set exit status code
|
||||
func SetExitStatus(n int) {
|
||||
exitMu.Lock()
|
||||
if exitStatus < n {
|
||||
exitStatus = n
|
||||
}
|
||||
exitMu.Unlock()
|
||||
}
|
||||
|
||||
// GetExitStatus get exit status code
|
||||
func GetExitStatus() int {
|
||||
return exitStatus
|
||||
}
|
23
main/commands/base/env.go
Normal file
23
main/commands/base/env.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// CommandEnvHolder is a struct holds the environment info of commands
|
||||
type CommandEnvHolder struct {
|
||||
Exec string
|
||||
}
|
||||
|
||||
// CommandEnv holds the environment info of commands
|
||||
var CommandEnv CommandEnvHolder
|
||||
|
||||
func init() {
|
||||
exec, err := os.Executable()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
CommandEnv.Exec = path.Base(exec)
|
||||
CommandEnv.Exec = "xray"
|
||||
}
|
88
main/commands/base/execute.go
Normal file
88
main/commands/base/execute.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// copied from "github.com/golang/go/main.go"
|
||||
|
||||
// Execute excute the commands
|
||||
func Execute() {
|
||||
buildCommandsText(RootCommand)
|
||||
flag.Parse()
|
||||
args := flag.Args()
|
||||
if len(args) < 1 {
|
||||
PrintUsage(os.Stderr, RootCommand)
|
||||
return
|
||||
}
|
||||
cmdName := args[0] // for error messages
|
||||
if args[0] == "help" {
|
||||
Help(os.Stdout, args[1:])
|
||||
return
|
||||
}
|
||||
|
||||
BigCmdLoop:
|
||||
for bigCmd := RootCommand; ; {
|
||||
for _, cmd := range bigCmd.Commands {
|
||||
if cmd.Name() != args[0] {
|
||||
continue
|
||||
}
|
||||
if len(cmd.Commands) > 0 {
|
||||
// test sub commands
|
||||
bigCmd = cmd
|
||||
args = args[1:]
|
||||
if len(args) == 0 {
|
||||
PrintUsage(os.Stderr, bigCmd)
|
||||
SetExitStatus(2)
|
||||
Exit()
|
||||
}
|
||||
if args[0] == "help" {
|
||||
// Accept 'go mod help' and 'go mod help foo' for 'go help mod' and 'go help mod foo'.
|
||||
Help(os.Stdout, append(strings.Split(cmdName, " "), args[1:]...))
|
||||
return
|
||||
}
|
||||
cmdName += " " + args[0]
|
||||
continue BigCmdLoop
|
||||
}
|
||||
if !cmd.Runnable() {
|
||||
continue
|
||||
}
|
||||
cmd.Flag.Usage = func() { cmd.Usage() }
|
||||
if cmd.CustomFlags {
|
||||
args = args[1:]
|
||||
} else {
|
||||
cmd.Flag.Parse(args[1:])
|
||||
args = cmd.Flag.Args()
|
||||
}
|
||||
|
||||
cmd.Run(cmd, args)
|
||||
Exit()
|
||||
return
|
||||
}
|
||||
helpArg := ""
|
||||
if i := strings.LastIndex(cmdName, " "); i >= 0 {
|
||||
helpArg = " " + cmdName[:i]
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "%s %s: unknown command\nRun '%s help%s' for usage.\n", CommandEnv.Exec, cmdName, CommandEnv.Exec, helpArg)
|
||||
SetExitStatus(2)
|
||||
Exit()
|
||||
}
|
||||
}
|
||||
|
||||
// Sort sorts the commands
|
||||
func Sort() {
|
||||
sort.Slice(RootCommand.Commands, func(i, j int) bool {
|
||||
return SortLessFunc(RootCommand.Commands[i], RootCommand.Commands[j])
|
||||
})
|
||||
}
|
||||
|
||||
// SortLessFunc used for sort commands list, can be override from outside
|
||||
var SortLessFunc = func(i, j *Command) bool {
|
||||
return i.Name() < j.Name()
|
||||
}
|
157
main/commands/base/help.go
Normal file
157
main/commands/base/help.go
Normal file
|
@ -0,0 +1,157 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
package base
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Help implements the 'help' command.
|
||||
func Help(w io.Writer, args []string) {
|
||||
cmd := RootCommand
|
||||
Args:
|
||||
for i, arg := range args {
|
||||
for _, sub := range cmd.Commands {
|
||||
if sub.Name() == arg {
|
||||
cmd = sub
|
||||
continue Args
|
||||
}
|
||||
}
|
||||
|
||||
// helpSuccess is the help command using as many args as possible that would succeed.
|
||||
helpSuccess := CommandEnv.Exec + " help"
|
||||
if i > 0 {
|
||||
helpSuccess += " " + strings.Join(args[:i], " ")
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "%s help %s: unknown help topic. Run '%s'.\n", CommandEnv.Exec, strings.Join(args, " "), helpSuccess)
|
||||
SetExitStatus(2) // failed at 'xray help cmd'
|
||||
Exit()
|
||||
}
|
||||
|
||||
if len(cmd.Commands) > 0 {
|
||||
PrintUsage(os.Stdout, cmd)
|
||||
} else {
|
||||
tmpl(os.Stdout, helpTemplate, makeTmplData(cmd))
|
||||
}
|
||||
}
|
||||
|
||||
var usageTemplate = `{{.Long | trim}}
|
||||
|
||||
Usage:
|
||||
|
||||
{{.Exec}} <command> [arguments]
|
||||
|
||||
The commands are:
|
||||
{{range .Commands}}{{if and (ne .Short "") (or (.Runnable) .Commands)}}
|
||||
{{.Name | printf "%-12s"}} {{.Short}}{{end}}{{end}}
|
||||
|
||||
Use "{{.Exec}} help{{with .LongName}} {{.}}{{end}} <command>" for more information about a command.
|
||||
`
|
||||
|
||||
// APPEND FOLLOWING TO 'usageTemplate' IF YOU WANT DOC,
|
||||
// A DOC TOPIC IS JUST A COMMAND NOT RUNNABLE:
|
||||
//
|
||||
// {{if eq (.UsageLine) (.Exec)}}
|
||||
// Additional help topics:
|
||||
// {{range .Commands}}{{if and (not .Runnable) (not .Commands)}}
|
||||
// {{.Name | printf "%-15s"}} {{.Short}}{{end}}{{end}}
|
||||
//
|
||||
// Use "{{.Exec}} help{{with .LongName}} {{.}}{{end}} <topic>" for more information about that topic.
|
||||
// {{end}}
|
||||
|
||||
var helpTemplate = `{{if .Runnable}}usage: {{.UsageLine}}
|
||||
|
||||
{{end}}{{.Long | trim}}
|
||||
`
|
||||
|
||||
// An errWriter wraps a writer, recording whether a write error occurred.
|
||||
type errWriter struct {
|
||||
w io.Writer
|
||||
err error
|
||||
}
|
||||
|
||||
func (w *errWriter) Write(b []byte) (int, error) {
|
||||
n, err := w.w.Write(b)
|
||||
if err != nil {
|
||||
w.err = err
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// tmpl executes the given template text on data, writing the result to w.
|
||||
func tmpl(w io.Writer, text string, data interface{}) {
|
||||
t := template.New("top")
|
||||
t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
|
||||
template.Must(t.Parse(text))
|
||||
ew := &errWriter{w: w}
|
||||
err := t.Execute(ew, data)
|
||||
if ew.err != nil {
|
||||
// I/O error writing. Ignore write on closed pipe.
|
||||
if strings.Contains(ew.err.Error(), "pipe") {
|
||||
SetExitStatus(1)
|
||||
Exit()
|
||||
}
|
||||
Fatalf("writing output: %v", ew.err)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func capitalize(s string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
r, n := utf8.DecodeRuneInString(s)
|
||||
return string(unicode.ToTitle(r)) + s[n:]
|
||||
}
|
||||
|
||||
// PrintUsage prints usage of cmd to w
|
||||
func PrintUsage(w io.Writer, cmd *Command) {
|
||||
bw := bufio.NewWriter(w)
|
||||
tmpl(bw, usageTemplate, makeTmplData(cmd))
|
||||
bw.Flush()
|
||||
}
|
||||
|
||||
// buildCommandsText build text of command and its children as template
|
||||
func buildCommandsText(cmd *Command) {
|
||||
buildCommandText(cmd)
|
||||
for _, cmd := range cmd.Commands {
|
||||
buildCommandsText(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// buildCommandText build command text as template
|
||||
func buildCommandText(cmd *Command) {
|
||||
cmd.UsageLine = buildText(cmd.UsageLine, makeTmplData(cmd))
|
||||
cmd.Short = buildText(cmd.Short, makeTmplData(cmd))
|
||||
cmd.Long = buildText(cmd.Long, makeTmplData(cmd))
|
||||
}
|
||||
|
||||
func buildText(text string, data interface{}) string {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
tmpl(buf, text, data)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
type tmplData struct {
|
||||
*Command
|
||||
*CommandEnvHolder
|
||||
}
|
||||
|
||||
func makeTmplData(cmd *Command) tmplData {
|
||||
return tmplData{
|
||||
Command: cmd,
|
||||
CommandEnvHolder: &CommandEnv,
|
||||
}
|
||||
}
|
16
main/commands/base/root.go
Normal file
16
main/commands/base/root.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package base
|
||||
|
||||
// RootCommand is the root command of all commands
|
||||
var RootCommand *Command
|
||||
|
||||
func init() {
|
||||
RootCommand = &Command{
|
||||
UsageLine: CommandEnv.Exec,
|
||||
Long: "The root command",
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterCommand register a command to RootCommand
|
||||
func RegisterCommand(cmd *Command) {
|
||||
RootCommand.Commands = append(RootCommand.Commands, cmd)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue