elvish/main.go

181 lines
3.2 KiB
Go
Raw Normal View History

2014-01-30 22:09:04 +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-06 05:12:05 +08:00
"flag"
2014-01-27 18:55:45 +08:00
"fmt"
2016-02-06 05:12:05 +08:00
"log"
2014-02-10 11:33:53 +08:00
"os"
"os/signal"
2014-02-10 11:33:53 +08:00
"os/user"
"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/errutil"
2014-10-30 03:50:10 +08:00
"github.com/elves/elvish/eval"
2016-01-29 10:04:31 +08:00
"github.com/elves/elvish/osutil"
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"
2013-06-16 16:08:08 +08:00
)
const (
sigchSize = 32
outChanSize = 32
outChanLeader = "▶ "
)
2016-02-06 05:12:05 +08:00
func usage() {
fmt.Println("usage: elvish [flags] [script]")
fmt.Println("flags:")
flag.PrintDefaults()
}
var (
debuglog = flag.String("debuglog", "", "a file to write debug log to")
help = flag.Bool("help", false, "show usage help and quit")
)
2015-01-26 23:43:32 +08:00
2016-02-06 03:16:22 +08:00
func main() {
defer rescue()
2016-02-06 05:12:05 +08:00
flag.Usage = usage
flag.Parse()
if *help {
usage()
os.Exit(0)
}
if *debuglog != "" {
f, err := os.OpenFile(*debuglog, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
log.Fatal(err)
}
defer f.Close()
edit.Logger = log.New(f, "[edit] ", log.LstdFlags)
}
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
}
}
2016-02-06 03:16:22 +08:00
func rescue() {
r := recover()
if r != nil {
print(sys.DumpStack())
println("execing recovery shell /bin/sh")
syscall.Exec("/bin/sh", []string{}, os.Environ())
2015-01-23 06:23:29 +08:00
}
}
// TODO(xiaq): Currently only the editor deals with signals.
func interact() {
ev, st := newEvalerAndStore()
datadir, err := store.EnsureDataDir()
printError(err)
if err == nil {
// XXX
err := ev.Source(datadir + "/rc.elv")
if err != nil && !os.IsNotExist(err) {
printError(err)
}
}
2014-01-31 19:18:10 +08:00
cmdNum := 0
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 = "???"
}
rpromptStr := username + "@" + hostname
2014-01-04 22:45:35 +08:00
sigch := make(chan os.Signal, sigchSize)
2016-01-28 07:22:54 +08:00
signal.Notify(sigch)
ed := edit.NewEditor(os.Stdin, sigch, ev, st)
2014-02-08 21:21:01 +08:00
for {
2014-01-31 19:18:10 +08:00
cmdNum++
name := fmt.Sprintf("<tty %d>", cmdNum)
prompt := func() string {
2016-01-29 10:04:31 +08:00
return osutil.Getwd() + "> "
}
rprompt := func() string {
return rpromptStr
}
2014-01-04 22:45:35 +08:00
lr := ed.ReadLine(prompt, rprompt)
2016-01-28 07:22:54 +08:00
// signal.Stop(sigch)
2014-01-31 19:18:10 +08:00
if lr.EOF {
break
} else if lr.Err != nil {
2014-03-16 23:15:45 +08:00
fmt.Println("Editor error:", lr.Err)
fmt.Println("My pid is", os.Getpid())
}
2013-09-18 22:58:50 +08:00
2015-02-10 19:22:01 +08:00
n, err := parse.Parse(name, lr.Line)
printError(err)
2015-02-10 19:22:01 +08:00
if err == nil {
err := ev.Eval(name, lr.Line, n)
2015-02-10 19:22:01 +08:00
printError(err)
2015-02-10 19:21:21 +08:00
}
}
2013-06-14 17:22:55 +08:00
}
func script(fname string) {
ev, _ := newEvalerAndStore()
err := ev.Source(fname)
if err != nil {
printError(err)
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")
}
2016-02-06 03:16:22 +08:00
var st *store.Store
if err == nil {
st, err = store.NewStore(dataDir)
if err != nil {
fmt.Fprintln(os.Stderr, "Warning: cannot connect to store:", err)
}
}
2016-02-06 03:16:22 +08:00
return eval.NewEvaler(st), st
}
2016-02-06 03:16:22 +08:00
func printError(err error) {
if err == nil {
return
}
if ce, ok := err.(*errutil.ContextualError); ok {
fmt.Fprint(os.Stderr, ce.Pprint())
} else {
eval.PprintError(err)
fmt.Println()
}
}