diff --git a/README.md b/README.md index 7c72a556..4cb243a0 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,7 @@ The adjective for elvish is also "elvish", not "elvishy" and definitely not "elv |eval|[![eval](https://gocover.io/_badge/github.com/elves/elvish/eval/)](https://gocover.io/github.com/elves/elvish/eval/)| |glob|[![glob](https://gocover.io/_badge/github.com/elves/elvish/glob/)](https://gocover.io/github.com/elves/elvish/glob/)| |parse|[![parse](https://gocover.io/_badge/github.com/elves/elvish/parse/)](https://gocover.io/github.com/elves/elvish/parse/)| +|run|[![run](https://gocover.io/_badge/github.com/elves/elvish/run/)](https://gocover.io/github.com/elves/elvish/run/)| |store|[![store](https://gocover.io/_badge/github.com/elves/elvish/store/)](https://gocover.io/github.com/elves/elvish/store/)| |sys|[![sys](https://gocover.io/_badge/github.com/elves/elvish/sys/)](https://gocover.io/github.com/elves/elvish/sys/)| |util|[![util](https://gocover.io/_badge/github.com/elves/elvish/util/)](https://gocover.io/github.com/elves/elvish/util/)| diff --git a/main.go b/main.go index 3a21d6b5..84e2bc79 100644 --- a/main.go +++ b/main.go @@ -2,236 +2,8 @@ // programming language with an extensible, friendly user interface. package main -import ( - "bufio" - "flag" - "fmt" - "io" - "os" - "os/signal" - "os/user" - "syscall" - - "github.com/elves/elvish/edit" - "github.com/elves/elvish/eval" - "github.com/elves/elvish/parse" - "github.com/elves/elvish/store" - "github.com/elves/elvish/sys" - "github.com/elves/elvish/util" -) - -var Logger = util.GetLogger("[main] ") - -var ( - log = flag.String("log", "", "a file to write debug log to") - dbname = flag.String("db", "", "path to the database") - help = flag.Bool("help", false, "show usage help and quit") -) - -func usage() { - fmt.Println("usage: elvish [flags] [script]") - fmt.Println("flags:") - flag.PrintDefaults() -} +import "github.com/elves/elvish/run" func main() { - defer rescue() - - flag.Usage = usage - flag.Parse() - args := flag.Args() - - if len(args) > 1 { - usage() - os.Exit(2) - } - - if *help { - usage() - os.Exit(0) - } - - if *log != "" { - err := util.SetOutputFile(*log) - if err != nil { - fmt.Println(err) - } - } - - go handleQuit() - go logSignals() - - ev, st := newEvalerAndStore() - defer func() { - err := st.Close() - if err != nil { - fmt.Println("failed to close database:", err) - } - }() - - if len(args) == 1 { - script(ev, args[0]) - } else { - interact(ev, st) - } -} - -func rescue() { - r := recover() - if r != nil { - println() - fmt.Println(r) - print(sys.DumpStack()) - println("\nexecing recovery shell /bin/sh") - syscall.Exec("/bin/sh", []string{"/bin/sh"}, os.Environ()) - } -} - -func script(ev *eval.Evaler, fname string) { - err := ev.Source(fname) - if err != nil { - printError(err) - os.Exit(1) - } -} - -func interact(ev *eval.Evaler, st *store.Store) { - // Build Editor. - sigch := make(chan os.Signal) - signal.Notify(sigch) - ed := edit.NewEditor(os.Stdin, sigch, ev, st) - - // Source rc.elv. - datadir, err := store.EnsureDataDir() - printError(err) - if err == nil { - // XXX - err := ev.Source(datadir + "/rc.elv") - if err != nil && !os.IsNotExist(err) { - printError(err) - } - } - - // Build prompt and rprompt. - username := "???" - user, err := user.Current() - if err == nil { - username = user.Username - } - hostname, err := os.Hostname() - if err != nil { - hostname = "???" - } - rpromptStr := username + "@" + hostname - prompt := func() string { - return util.Getwd() + "> " - } - rprompt := func() string { - return rpromptStr - } - - // Build readLine function. - readLine := func() edit.LineRead { - return ed.ReadLine(prompt, rprompt) - } - - usingBasic := false - - if !sys.IsATTY(0) { - readLine = basicReadLine - usingBasic = true - } - - cmdNum := 0 - - for { - cmdNum++ - // name := fmt.Sprintf("", cmdNum) - - lr := readLine() - - if lr.EOF { - break - } else if lr.Err != nil { - fmt.Println("Editor error:", lr.Err) - if !usingBasic { - fmt.Println("Falling back to basic line editor") - readLine = basicReadLine - usingBasic = true - } - continue - } - - n, err := parse.Parse(lr.Line) - printError(err) - - if err == nil { - err := ev.EvalInteractive(lr.Line, n) - printError(err) - } - } -} - -func basicReadLine() edit.LineRead { - stdin := bufio.NewReaderSize(os.Stdin, 0) - line, err := stdin.ReadString('\n') - if err == nil { - return edit.LineRead{Line: line} - } else if err == io.EOF { - return edit.LineRead{EOF: true} - } else { - return edit.LineRead{Err: err} - } -} - -func logSignals() { - sigs := make(chan os.Signal) - signal.Notify(sigs) - for sig := range sigs { - Logger.Println("signal", sig) - } -} - -func handleQuit() { - quitSigs := make(chan os.Signal) - signal.Notify(quitSigs, syscall.SIGQUIT) - <-quitSigs - fmt.Print(sys.DumpStack()) - os.Exit(3) -} - -func newEvalerAndStore() (*eval.Evaler, *store.Store) { - dataDir, err := store.EnsureDataDir() - if err != nil { - fmt.Fprintln(os.Stderr, "Warning: cannot create data dir ~/.elvish") - } - - var st *store.Store - if err == nil { - db := *dbname - if db == "" { - db = dataDir + "/db" - } - st, err = store.NewStore(db) - if err != nil { - fmt.Fprintln(os.Stderr, "Warning: cannot connect to store:", err) - } - } - - return eval.NewEvaler(st), st -} - -func printError(err error) { - if err == nil { - return - } - switch err := err.(type) { - case *util.Errors: - for _, e := range err.Errors { - printError(e) - } - default: - eval.PprintError(err) - fmt.Println() - } + run.Main() } diff --git a/run/run.go b/run/run.go new file mode 100644 index 00000000..cd347dee --- /dev/null +++ b/run/run.go @@ -0,0 +1,237 @@ +// Package run is the entry point of elvish. +package run + +import ( + "bufio" + "flag" + "fmt" + "io" + "os" + "os/signal" + "os/user" + "syscall" + + "github.com/elves/elvish/edit" + "github.com/elves/elvish/eval" + "github.com/elves/elvish/parse" + "github.com/elves/elvish/store" + "github.com/elves/elvish/sys" + "github.com/elves/elvish/util" +) + +var Logger = util.GetLogger("[main] ") + +var ( + log = flag.String("log", "", "a file to write debug log to") + dbname = flag.String("db", "", "path to the database") + help = flag.Bool("help", false, "show usage help and quit") +) + +func usage() { + fmt.Println("usage: elvish [flags] [script]") + fmt.Println("flags:") + flag.PrintDefaults() +} + +// Main is the entry point of elvish. +func Main() { + defer rescue() + + flag.Usage = usage + flag.Parse() + args := flag.Args() + + if len(args) > 1 { + usage() + os.Exit(2) + } + + if *help { + usage() + os.Exit(0) + } + + if *log != "" { + err := util.SetOutputFile(*log) + if err != nil { + fmt.Println(err) + } + } + + go handleQuit() + go logSignals() + + ev, st := newEvalerAndStore() + defer func() { + err := st.Close() + if err != nil { + fmt.Println("failed to close database:", err) + } + }() + + if len(args) == 1 { + script(ev, args[0]) + } else { + interact(ev, st) + } +} + +func rescue() { + r := recover() + if r != nil { + println() + fmt.Println(r) + print(sys.DumpStack()) + println("\nexecing recovery shell /bin/sh") + syscall.Exec("/bin/sh", []string{"/bin/sh"}, os.Environ()) + } +} + +func script(ev *eval.Evaler, fname string) { + err := ev.Source(fname) + if err != nil { + printError(err) + os.Exit(1) + } +} + +func interact(ev *eval.Evaler, st *store.Store) { + // Build Editor. + sigch := make(chan os.Signal) + signal.Notify(sigch) + ed := edit.NewEditor(os.Stdin, sigch, ev, st) + + // Source rc.elv. + datadir, err := store.EnsureDataDir() + printError(err) + if err == nil { + // XXX + err := ev.Source(datadir + "/rc.elv") + if err != nil && !os.IsNotExist(err) { + printError(err) + } + } + + // Build prompt and rprompt. + username := "???" + user, err := user.Current() + if err == nil { + username = user.Username + } + hostname, err := os.Hostname() + if err != nil { + hostname = "???" + } + rpromptStr := username + "@" + hostname + prompt := func() string { + return util.Getwd() + "> " + } + rprompt := func() string { + return rpromptStr + } + + // Build readLine function. + readLine := func() edit.LineRead { + return ed.ReadLine(prompt, rprompt) + } + + usingBasic := false + + if !sys.IsATTY(0) { + readLine = basicReadLine + usingBasic = true + } + + cmdNum := 0 + + for { + cmdNum++ + // name := fmt.Sprintf("", cmdNum) + + lr := readLine() + + if lr.EOF { + break + } else if lr.Err != nil { + fmt.Println("Editor error:", lr.Err) + if !usingBasic { + fmt.Println("Falling back to basic line editor") + readLine = basicReadLine + usingBasic = true + } + continue + } + + n, err := parse.Parse(lr.Line) + printError(err) + + if err == nil { + err := ev.EvalInteractive(lr.Line, n) + printError(err) + } + } +} + +func basicReadLine() edit.LineRead { + stdin := bufio.NewReaderSize(os.Stdin, 0) + line, err := stdin.ReadString('\n') + if err == nil { + return edit.LineRead{Line: line} + } else if err == io.EOF { + return edit.LineRead{EOF: true} + } else { + return edit.LineRead{Err: err} + } +} + +func logSignals() { + sigs := make(chan os.Signal) + signal.Notify(sigs) + for sig := range sigs { + Logger.Println("signal", sig) + } +} + +func handleQuit() { + quitSigs := make(chan os.Signal) + signal.Notify(quitSigs, syscall.SIGQUIT) + <-quitSigs + fmt.Print(sys.DumpStack()) + os.Exit(3) +} + +func newEvalerAndStore() (*eval.Evaler, *store.Store) { + dataDir, err := store.EnsureDataDir() + if err != nil { + fmt.Fprintln(os.Stderr, "Warning: cannot create data dir ~/.elvish") + } + + var st *store.Store + if err == nil { + db := *dbname + if db == "" { + db = dataDir + "/db" + } + st, err = store.NewStore(db) + if err != nil { + fmt.Fprintln(os.Stderr, "Warning: cannot connect to store:", err) + } + } + + return eval.NewEvaler(st), st +} + +func printError(err error) { + if err == nil { + return + } + switch err := err.(type) { + case *util.Errors: + for _, e := range err.Errors { + printError(e) + } + default: + eval.PprintError(err) + fmt.Println() + } +}