// Package prog provides the entry point to Elvish. Its subpackages correspond // to subprograms of Elvish.
package prog // This package sets up the basic environment and calls the appropriate // "subprogram", one of the daemon, the terminal interface, or the web // interface. import ( ) // Default port on which the web interface runs. The number is chosen because it // resembles "elvi". const defaultWebPort = 3171 // DeprecationLevel is a global flag that controls which deprecations to show. // If its value is X, Elvish shows deprecations that should be shown for version // 0.X. var DeprecationLevel = 15 // SetDeprecationLevel sets ShowDeprecations to the given value, and returns a // function to restore the old value. func ( int) func() { := DeprecationLevel DeprecationLevel = return func() { DeprecationLevel = } } // Flags keeps command-line flags. type Flags struct { Log, CPUProfile string Help, Version, BuildInfo, JSON bool CodeInArg, CompileOnly, NoRc bool Web bool Port int Daemon bool Forked int Bin, DB, Sock string } func ( io.Writer, *Flags) *flag.FlagSet { := flag.NewFlagSet("elvish", flag.ContinueOnError) .SetOutput() .Usage = func() { usage(, ) } .StringVar(&.Log, "log", "", "a file to write debug log to except for the daemon") .StringVar(&.CPUProfile, "cpuprofile", "", "write cpu profile to file") .BoolVar(&.Help, "help", false, "show usage help and quit") .BoolVar(&.Version, "version", false, "show version and quit") .BoolVar(&.BuildInfo, "buildinfo", false, "show build info and quit") .BoolVar(&.JSON, "json", false, "show output in JSON. Useful with -buildinfo.") // The `-i` option is for compatibility with POSIX shells so that programs, such as the `script` // command, will work when asked to launch an interactive Elvish shell. .Bool("i", false, "force interactive mode; currently ignored") .BoolVar(&.CodeInArg, "c", false, "take first argument as code to execute") .BoolVar(&.CompileOnly, "compileonly", false, "Parse/Compile but do not execute") .BoolVar(&.NoRc, "norc", false, "run elvish without invoking rc.elv") .BoolVar(&.Web, "web", false, "run backend of web interface") .IntVar(&.Port, "port", defaultWebPort, "the port of the web backend") .BoolVar(&.Daemon, "daemon", false, "run daemon instead of shell") .StringVar(&.Bin, "bin", "", "path to the elvish binary") .StringVar(&.DB, "db", "", "path to the database") .StringVar(&.Sock, "sock", "", "path to the daemon socket") .IntVar(&DeprecationLevel, "deprecation-level", DeprecationLevel, "show warnings for all features deprecated as of version 0.X") return } func ( io.Writer, *flag.FlagSet) { fmt.Fprintln(, "Usage: elvish [flags] [script]") fmt.Fprintln(, "Supported flags:") .PrintDefaults() } // Run parses command-line flags and runs the first applicable subprogram. It // returns the exit status of the program. func ( [3]*os.File, []string, ...Program) int { := &Flags{} := newFlagSet([2], ) := .Parse([1:]) if != nil { // Error and usage messages are already shown. return 2 } // Handle flags common to all subprograms. if .CPUProfile != "" { , := os.Create(.CPUProfile) if != nil { fmt.Fprintln([2], "Warning: cannot create CPU profile:", ) fmt.Fprintln([2], "Continuing without CPU profiling.") } else { pprof.StartCPUProfile() defer pprof.StopCPUProfile() } } if .Daemon { // We expect our stdout file handle is open on a unique log file for the daemon to write its // log messages. See daemon.Spawn() in pkg/daemon. logutil.SetOutput([1]) } else if .Log != "" { = logutil.SetOutputFile(.Log) if != nil { fmt.Fprintln([2], ) } } if .Help { .SetOutput([1]) usage([1], ) return 0 } := findProgram(, ) if == nil { fmt.Fprintln([2], "program bug: no suitable subprogram") return 2 } = .Run(, , .Args()) if == nil { return 0 } if := .Error(); != "" { fmt.Fprintln([2], ) } switch err := .(type) { case badUsageError: usage([2], ) case exitError: return .exit } return 2 } func ( *Flags, []Program) Program { for , := range { if .ShouldRun() { return } } return nil } // BadUsage returns an error that may be returned by Program.Main, which // requests the main program to print out a message, the usage information and // exit with 2. func ( string) error { return badUsageError{} } type badUsageError struct{ msg string } func ( badUsageError) () string { return .msg } // Exit returns an error that may be returned by Program.Main, which requests the // main program to exit with the given code. If the exit code is 0, it returns nil. func ( int) error { if == 0 { return nil } return exitError{} } type exitError struct{ exit int } func ( exitError) () string { return "" } // Program represents a subprogram. type Program interface { // ShouldRun returns whether the subprogram should run. ShouldRun(f *Flags) bool // Run runs the subprogram. Run(fds [3]*os.File, f *Flags, args []string) error }