|
|
@@ -1,14 +1,25 @@
|
|
|
package shell
|
|
|
|
|
|
import (
|
|
|
+ "os"
|
|
|
"path/filepath"
|
|
|
"strings"
|
|
|
"syscall"
|
|
|
)
|
|
|
|
|
|
type Executor struct {
|
|
|
- cwd string // 当前目录
|
|
|
- pg *processGroup // --进程组
|
|
|
+ cwd string // 当前目录
|
|
|
+ prevCwd string // 上次目录
|
|
|
+ pg *processGroup // --进程组
|
|
|
+}
|
|
|
+
|
|
|
+type PathError struct {
|
|
|
+ Path string
|
|
|
+ Info string
|
|
|
+}
|
|
|
+
|
|
|
+func (e *PathError) Error() string {
|
|
|
+ return e.Info + ": " + e.Path
|
|
|
}
|
|
|
|
|
|
func NewExecutor() *Executor {
|
|
|
@@ -16,14 +27,15 @@ func NewExecutor() *Executor {
|
|
|
}
|
|
|
|
|
|
func (e *Executor) Exec(p ExecuteParams) (*ExecuteResult, error) {
|
|
|
- if isCD(p.Cmd) {
|
|
|
- dir, err := resolveCD(e.cwd, p.Cmd)
|
|
|
+ if e.isCD(p.Cmd) {
|
|
|
+ dir, err := e.resolveCD(e.cwd, p.Cmd)
|
|
|
if err != nil {
|
|
|
return &ExecuteResult{
|
|
|
- Stderr: err.Error() + "\n",
|
|
|
+ Stderr: err.Error(),
|
|
|
ExitCode: 1,
|
|
|
}, nil
|
|
|
}
|
|
|
+ e.prevCwd = e.cwd
|
|
|
e.cwd = dir
|
|
|
return &ExecuteResult{ExitCode: 0, Cwd: e.cwd}, nil
|
|
|
}
|
|
|
@@ -49,23 +61,51 @@ func (e *Executor) Exec(p ExecuteParams) (*ExecuteResult, error) {
|
|
|
return result, err
|
|
|
}
|
|
|
|
|
|
-func isCD(cmd string) bool {
|
|
|
+func (e *Executor) isCD(cmd string) bool {
|
|
|
s := strings.TrimSpace(cmd)
|
|
|
return s == "cd" || strings.HasPrefix(s, "cd ")
|
|
|
}
|
|
|
|
|
|
-func resolveCD(cwd, cmd string) (string, error) {
|
|
|
+func (e *Executor) resolveCD(cwd, cmd string) (string, error) {
|
|
|
fields := strings.Fields(cmd)
|
|
|
if len(fields) == 1 { // 只有"cd", 没有参数
|
|
|
return "/", nil
|
|
|
}
|
|
|
|
|
|
path := fields[1]
|
|
|
+ switch path {
|
|
|
+ case "~":
|
|
|
+ home := os.Getenv("HOME")
|
|
|
+ if home == "" {
|
|
|
+ home = "/"
|
|
|
+ }
|
|
|
+ return home, nil
|
|
|
+ case "-":
|
|
|
+ if e.prevCwd == "" {
|
|
|
+ return cwd, nil
|
|
|
+ }
|
|
|
+ return e.prevCwd, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ var target string
|
|
|
if filepath.IsAbs(path) { // 绝对路径
|
|
|
- return filepath.Clean(path), nil
|
|
|
+ target = filepath.Clean(path)
|
|
|
+ } else { // 相对路径
|
|
|
+ target = filepath.Clean(filepath.Join(cwd, path))
|
|
|
+ }
|
|
|
+
|
|
|
+ info, err := os.Stat(target)
|
|
|
+ if err != nil {
|
|
|
+ if os.IsNotExist(err) {
|
|
|
+ return "", &PathError{Path: target, Info: "directory not exist"}
|
|
|
+ }
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+ if !info.IsDir() {
|
|
|
+ return "", &PathError{Path: target, Info: "not a directory"}
|
|
|
}
|
|
|
|
|
|
- return filepath.Clean(filepath.Join(cwd, path)), nil // 相对路径
|
|
|
+ return target, nil
|
|
|
}
|
|
|
|
|
|
func (e *Executor) Interrupt() error {
|