1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
// Package program provides the entry point to Elvish. Its subpackages
// correspond to subprograms of Elvish.
package program
// This package sets up the basic environment and calls the appropriate
// "subprogram", one of the daemon, the terminal interface, or the web
// interface.
import (
"flag"
"fmt"
"io"
"log"
"os"
"runtime/pprof"
"strconv"
"github.com/elves/elvish/program/daemon"
"github.com/elves/elvish/program/shell"
"github.com/elves/elvish/program/web"
"github.com/elves/elvish/util"
)
// defaultPort is the default port on which the web interface runs. The number
// is chosen because it resembles "elvi".
const defaultWebPort = 3171
var logger = util.GetLogger("[main] ")
type flagSet struct {
flag.FlagSet
Log, LogPrefix, CPUProfile string
Help, Version, BuildInfo, JSON bool
CodeInArg, CompileOnly, NoRc bool
Web bool
Port int
Daemon bool
Forked int
Bin, DB, Sock string
}
func newFlagSet() *flagSet {
f := flagSet{}
f.Init("elvish", flag.ContinueOnError)
f.Usage = func() {
usage(os.Stderr, &f)
}
f.StringVar(&f.Log, "log", "", "a file to write debug log to")
f.StringVar(&f.LogPrefix, "logprefix", "", "the prefix for the daemon log file")
f.StringVar(&f.CPUProfile, "cpuprofile", "", "write cpu profile to file")
f.BoolVar(&f.Help, "help", false, "show usage help and quit")
f.BoolVar(&f.Version, "version", false, "show version and quit")
f.BoolVar(&f.BuildInfo, "buildinfo", false, "show build info and quit")
f.BoolVar(&f.JSON, "json", false, "show output in JSON. Useful with -buildinfo.")
f.BoolVar(&f.CodeInArg, "c", false, "take first argument as code to execute")
f.BoolVar(&f.CompileOnly, "compileonly", false, "Parse/Compile but do not execute")
f.BoolVar(&f.NoRc, "norc", false, "run elvish without invoking rc.elv")
f.BoolVar(&f.Web, "web", false, "run backend of web interface")
f.IntVar(&f.Port, "port", defaultWebPort, "the port of the web backend")
f.BoolVar(&f.Daemon, "daemon", false, "run daemon instead of shell")
f.StringVar(&f.Bin, "bin", "", "path to the elvish binary")
f.StringVar(&f.DB, "db", "", "path to the database")
f.StringVar(&f.Sock, "sock", "", "path to the daemon socket")
return &f
}
// usage prints usage to the specified output. It modifies the flagSet; there is
// no API for getting the current output of a flag.FlagSet, so we can neither
// use the current output of f to output our own usage string, nor restore the
// previous value of f's output.
func usage(out io.Writer, f *flagSet) {
f.SetOutput(out)
fmt.Fprintln(out, "Usage: elvish [flags] [script]")
fmt.Fprintln(out, "Supported flags:")
f.PrintDefaults()
}
func Main(allArgs []string) int {
flag := newFlagSet()
err := flag.Parse(allArgs[1:])
if err != nil {
// Error and usage messages are already shown.
return 2
}
// Handle flags common to all subprograms.
if flag.CPUProfile != "" {
f, err := os.Create(flag.CPUProfile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
if flag.Log != "" {
err = util.SetOutputFile(flag.Log)
} else if flag.LogPrefix != "" {
err = util.SetOutputFile(flag.LogPrefix + strconv.Itoa(os.Getpid()))
}
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
return FindProgram(flag).Main(flag.Args())
}
// Program represents a subprogram.
type Program interface {
// Main calls the subprogram with arguments. The return value will be used
// as the exit status of the entire program.
Main(args []string) int
}
// FindProgram finds a suitable Program according to flags. It does not have any
// side effects.
func FindProgram(flag *flagSet) Program {
switch {
case flag.Help:
return ShowHelp{flag}
case flag.Version:
return ShowVersion{}
case flag.BuildInfo:
return ShowBuildInfo{flag.JSON}
case flag.Daemon:
if len(flag.Args()) > 0 {
return ShowCorrectUsage{"arguments are not allowed with -daemon", flag}
}
return Daemon{inner: &daemon.Daemon{
BinPath: flag.Bin,
DbPath: flag.DB,
SockPath: flag.Sock,
LogPathPrefix: flag.LogPrefix,
}}
case flag.Web:
if len(flag.Args()) > 0 {
return ShowCorrectUsage{"arguments are not allowed with -web", flag}
}
if flag.CodeInArg {
return ShowCorrectUsage{"-c cannot be used together with -web", flag}
}
return web.New(flag.Bin, flag.Sock, flag.DB, flag.Port)
default:
return shell.New(flag.Bin, flag.Sock, flag.DB, flag.CodeInArg, flag.CompileOnly, flag.NoRc)
}
}
|