2016-02-21 05:51:55 +08:00
|
|
|
// Elvish is an experimental Unix shell. It tries to incorporate a powerful
|
2014-02-08 19:26:06 +08:00
|
|
|
// programming language with an extensible, friendly user interface.
|
2013-06-14 17:22:55 +08:00
|
|
|
package main
|
|
|
|
|
2013-06-16 16:08:08 +08:00
|
|
|
import (
|
2016-02-12 02:32:40 +08:00
|
|
|
"bufio"
|
2016-02-06 05:12:05 +08:00
|
|
|
"flag"
|
2014-01-27 18:55:45 +08:00
|
|
|
"fmt"
|
2016-02-12 02:32:40 +08:00
|
|
|
"io"
|
2014-02-10 11:33:53 +08:00
|
|
|
"os"
|
2014-03-10 21:23:42 +08:00
|
|
|
"os/signal"
|
2014-02-10 11:33:53 +08:00
|
|
|
"os/user"
|
2016-01-28 08:25:17 +08:00
|
|
|
"syscall"
|
2014-02-10 11:33:53 +08:00
|
|
|
|
2014-10-30 03:50:10 +08:00
|
|
|
"github.com/elves/elvish/edit"
|
|
|
|
"github.com/elves/elvish/eval"
|
2016-01-25 01:10:54 +08:00
|
|
|
"github.com/elves/elvish/parse"
|
2015-01-26 23:43:32 +08:00
|
|
|
"github.com/elves/elvish/store"
|
2016-02-03 09:19:09 +08:00
|
|
|
"github.com/elves/elvish/sys"
|
2016-02-17 02:14:05 +08:00
|
|
|
"github.com/elves/elvish/util"
|
2013-06-16 16:08:08 +08:00
|
|
|
)
|
|
|
|
|
2014-03-03 17:53:23 +08:00
|
|
|
const (
|
2014-09-21 06:30:26 +08:00
|
|
|
sigchSize = 32
|
|
|
|
outChanSize = 32
|
|
|
|
outChanLeader = "▶ "
|
2014-03-03 17:53:23 +08:00
|
|
|
)
|
|
|
|
|
2016-02-17 02:14:05 +08:00
|
|
|
var Logger = util.GetLogger("[main] ")
|
2016-02-10 01:38:26 +08:00
|
|
|
|
2016-02-06 05:12:05 +08:00
|
|
|
func usage() {
|
|
|
|
fmt.Println("usage: elvish [flags] [script]")
|
|
|
|
fmt.Println("flags:")
|
|
|
|
flag.PrintDefaults()
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2016-02-14 04:05:35 +08:00
|
|
|
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")
|
2016-02-06 05:12:05 +08:00
|
|
|
)
|
2015-01-26 23:43:32 +08:00
|
|
|
|
2016-02-06 03:16:22 +08:00
|
|
|
func main() {
|
|
|
|
defer rescue()
|
2015-02-24 01:36:46 +08:00
|
|
|
|
2016-02-06 05:12:05 +08:00
|
|
|
flag.Usage = usage
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
if *help {
|
|
|
|
usage()
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
2016-02-14 04:05:35 +08:00
|
|
|
if *log != "" {
|
2016-02-17 02:14:05 +08:00
|
|
|
err := util.SetOutputFile(*log)
|
2016-02-06 05:12:05 +08:00
|
|
|
if err != nil {
|
2016-02-14 04:05:35 +08:00
|
|
|
fmt.Println(err)
|
2016-02-06 05:12:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-11 04:48:08 +08:00
|
|
|
go handleQuit()
|
2016-02-10 01:38:26 +08:00
|
|
|
go logSignals()
|
|
|
|
|
2016-02-06 05:12:05 +08:00
|
|
|
args := flag.Args()
|
|
|
|
switch len(args) {
|
|
|
|
case 0:
|
2016-02-06 03:16:22 +08:00
|
|
|
interact()
|
2016-02-06 05:12:05 +08:00
|
|
|
case 1:
|
|
|
|
script(args[0])
|
2016-02-06 03:16:22 +08:00
|
|
|
default:
|
2016-02-06 05:12:05 +08:00
|
|
|
usage()
|
|
|
|
os.Exit(2)
|
2016-02-06 03:16:22 +08:00
|
|
|
}
|
2015-01-23 06:15:54 +08:00
|
|
|
}
|
|
|
|
|
2016-02-06 03:16:22 +08:00
|
|
|
func rescue() {
|
|
|
|
r := recover()
|
|
|
|
if r != nil {
|
2016-02-15 03:06:34 +08:00
|
|
|
println()
|
|
|
|
fmt.Println(r)
|
2016-02-06 03:16:22 +08:00
|
|
|
print(sys.DumpStack())
|
2016-02-06 05:34:47 +08:00
|
|
|
println("\nexecing recovery shell /bin/sh")
|
|
|
|
syscall.Exec("/bin/sh", []string{"/bin/sh"}, os.Environ())
|
2015-01-23 06:23:29 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-23 06:15:54 +08:00
|
|
|
// TODO(xiaq): Currently only the editor deals with signals.
|
|
|
|
func interact() {
|
2015-02-26 07:40:35 +08:00
|
|
|
ev, st := newEvalerAndStore()
|
2016-02-20 09:27:26 +08:00
|
|
|
defer closeStore(st)
|
2016-02-10 03:15:47 +08:00
|
|
|
|
|
|
|
sigch := make(chan os.Signal, sigchSize)
|
|
|
|
signal.Notify(sigch)
|
|
|
|
|
|
|
|
ed := edit.NewEditor(os.Stdin, sigch, ev, st)
|
|
|
|
|
2015-02-09 21:01:13 +08:00
|
|
|
datadir, err := store.EnsureDataDir()
|
|
|
|
printError(err)
|
|
|
|
if err == nil {
|
2015-07-08 06:12:24 +08:00
|
|
|
// XXX
|
2016-01-31 08:40:49 +08:00
|
|
|
err := ev.Source(datadir + "/rc.elv")
|
2015-02-09 21:01:13 +08:00
|
|
|
if err != nil && !os.IsNotExist(err) {
|
|
|
|
printError(err)
|
|
|
|
}
|
|
|
|
}
|
2015-01-23 06:15:54 +08:00
|
|
|
|
2014-01-31 19:18:10 +08:00
|
|
|
cmdNum := 0
|
2013-10-17 16:45:26 +08:00
|
|
|
|
2014-01-04 22:45:35 +08:00
|
|
|
username := "???"
|
|
|
|
user, err := user.Current()
|
|
|
|
if err == nil {
|
|
|
|
username = user.Username
|
|
|
|
}
|
|
|
|
hostname, err := os.Hostname()
|
|
|
|
if err != nil {
|
|
|
|
hostname = "???"
|
|
|
|
}
|
2014-03-10 14:50:53 +08:00
|
|
|
rpromptStr := username + "@" + hostname
|
2016-02-12 02:32:40 +08:00
|
|
|
prompt := func() string {
|
2016-02-17 02:14:05 +08:00
|
|
|
return util.Getwd() + "> "
|
2016-02-12 02:32:40 +08:00
|
|
|
}
|
|
|
|
rprompt := func() string {
|
|
|
|
return rpromptStr
|
|
|
|
}
|
|
|
|
|
|
|
|
readLine := func() edit.LineRead {
|
|
|
|
return ed.ReadLine(prompt, rprompt)
|
|
|
|
}
|
|
|
|
|
|
|
|
usingBasic := false
|
|
|
|
|
|
|
|
if !sys.IsATTY(0) {
|
|
|
|
readLine = basicReadLine
|
|
|
|
usingBasic = true
|
|
|
|
}
|
2014-01-04 22:45:35 +08:00
|
|
|
|
2013-07-29 14:39:21 +08:00
|
|
|
for {
|
2014-01-31 19:18:10 +08:00
|
|
|
cmdNum++
|
2016-02-09 01:51:06 +08:00
|
|
|
// name := fmt.Sprintf("<tty %d>", cmdNum)
|
2013-08-15 16:36:29 +08:00
|
|
|
|
2016-02-12 02:32:40 +08:00
|
|
|
lr := readLine()
|
2016-01-28 07:22:54 +08:00
|
|
|
// signal.Stop(sigch)
|
2013-09-03 22:22:28 +08:00
|
|
|
|
2014-01-31 19:18:10 +08:00
|
|
|
if lr.EOF {
|
2013-07-29 14:39:21 +08:00
|
|
|
break
|
2013-09-03 22:22:28 +08:00
|
|
|
} else if lr.Err != nil {
|
2014-03-16 23:15:45 +08:00
|
|
|
fmt.Println("Editor error:", lr.Err)
|
2016-02-12 02:32:40 +08:00
|
|
|
if !usingBasic {
|
|
|
|
fmt.Println("Falling back to basic line editor")
|
|
|
|
readLine = basicReadLine
|
|
|
|
usingBasic = true
|
|
|
|
}
|
|
|
|
continue
|
2013-07-29 14:39:21 +08:00
|
|
|
}
|
2013-09-18 22:58:50 +08:00
|
|
|
|
2016-02-06 07:08:39 +08:00
|
|
|
n, err := parse.Parse(lr.Line)
|
2015-02-10 19:22:01 +08:00
|
|
|
printError(err)
|
2013-09-18 19:46:34 +08:00
|
|
|
|
2015-02-10 19:22:01 +08:00
|
|
|
if err == nil {
|
2016-02-09 01:51:06 +08:00
|
|
|
err := ev.EvalInteractive(lr.Line, n)
|
2015-02-10 19:22:01 +08:00
|
|
|
printError(err)
|
2015-02-10 19:21:21 +08:00
|
|
|
}
|
2013-07-29 14:39:21 +08:00
|
|
|
}
|
2013-06-14 17:22:55 +08:00
|
|
|
}
|
2014-05-25 17:55:53 +08:00
|
|
|
|
2016-02-12 02:32:40 +08:00
|
|
|
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}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-10 01:38:26 +08:00
|
|
|
func logSignals() {
|
|
|
|
sigs := make(chan os.Signal)
|
|
|
|
signal.Notify(sigs)
|
|
|
|
for sig := range sigs {
|
|
|
|
Logger.Println("signal", sig)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-11 04:48:08 +08:00
|
|
|
func handleQuit() {
|
2016-02-10 01:38:26 +08:00
|
|
|
quitSigs := make(chan os.Signal)
|
|
|
|
signal.Notify(quitSigs, syscall.SIGQUIT)
|
2016-02-11 04:49:39 +08:00
|
|
|
<-quitSigs
|
|
|
|
fmt.Print(sys.DumpStack())
|
2016-02-11 04:48:08 +08:00
|
|
|
os.Exit(3)
|
2016-02-10 01:38:26 +08:00
|
|
|
}
|
|
|
|
|
2015-01-23 06:15:54 +08:00
|
|
|
func script(fname string) {
|
2016-02-20 09:27:26 +08:00
|
|
|
ev, st := newEvalerAndStore()
|
|
|
|
defer closeStore(st)
|
2016-01-31 08:40:49 +08:00
|
|
|
err := ev.Source(fname)
|
|
|
|
if err != nil {
|
|
|
|
printError(err)
|
2014-05-25 17:55:53 +08:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-06 03:16:22 +08:00
|
|
|
func newEvalerAndStore() (*eval.Evaler, *store.Store) {
|
|
|
|
dataDir, err := store.EnsureDataDir()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, "Warning: cannot create data dir ~/.elvish")
|
|
|
|
}
|
2014-05-25 17:55:53 +08:00
|
|
|
|
2016-02-06 03:16:22 +08:00
|
|
|
var st *store.Store
|
|
|
|
if err == nil {
|
2016-02-13 07:35:33 +08:00
|
|
|
db := *dbname
|
|
|
|
if db == "" {
|
|
|
|
db = dataDir + "/db"
|
|
|
|
}
|
|
|
|
st, err = store.NewStore(db)
|
2016-02-06 03:16:22 +08:00
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, "Warning: cannot connect to store:", err)
|
|
|
|
}
|
2016-01-28 08:25:17 +08:00
|
|
|
}
|
|
|
|
|
2016-02-06 03:16:22 +08:00
|
|
|
return eval.NewEvaler(st), st
|
|
|
|
}
|
2016-01-28 08:25:17 +08:00
|
|
|
|
2016-02-20 09:27:26 +08:00
|
|
|
func closeStore(st *store.Store) {
|
|
|
|
err := st.Close()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("failed to close database:", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-06 03:16:22 +08:00
|
|
|
func printError(err error) {
|
|
|
|
if err == nil {
|
|
|
|
return
|
|
|
|
}
|
2016-02-06 07:08:39 +08:00
|
|
|
switch err := err.(type) {
|
2016-02-17 02:14:05 +08:00
|
|
|
case *util.Errors:
|
2016-02-06 07:08:39 +08:00
|
|
|
for _, e := range err.Errors {
|
|
|
|
printError(e)
|
|
|
|
}
|
|
|
|
default:
|
2016-02-06 03:16:22 +08:00
|
|
|
eval.PprintError(err)
|
|
|
|
fmt.Println()
|
2014-05-25 17:55:53 +08:00
|
|
|
}
|
|
|
|
}
|