2016-08-13 12:49:04 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2018-03-26 18:22:18 +00:00
|
|
|
"bufio"
|
2016-08-13 12:49:04 +00:00
|
|
|
"fmt"
|
2018-03-26 18:22:18 +00:00
|
|
|
"io"
|
2016-08-13 12:49:04 +00:00
|
|
|
"log"
|
|
|
|
"os"
|
2018-04-03 19:56:38 +00:00
|
|
|
"os/exec"
|
2020-04-13 16:32:42 +00:00
|
|
|
"os/signal"
|
2018-07-11 17:49:55 +00:00
|
|
|
"path/filepath"
|
2016-08-13 12:49:04 +00:00
|
|
|
"strings"
|
2020-06-11 01:11:40 +00:00
|
|
|
"syscall"
|
2018-06-09 19:02:09 +00:00
|
|
|
"time"
|
2016-08-13 12:49:04 +00:00
|
|
|
)
|
|
|
|
|
2017-05-15 09:30:50 +00:00
|
|
|
type cmdItem struct {
|
2017-11-19 18:55:13 +00:00
|
|
|
prefix string
|
|
|
|
value string
|
2017-05-15 09:30:50 +00:00
|
|
|
}
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
type app struct {
|
2018-05-15 21:16:49 +00:00
|
|
|
ui *ui
|
|
|
|
nav *nav
|
2018-06-09 19:02:09 +00:00
|
|
|
ticker *time.Ticker
|
2020-12-27 01:23:03 +00:00
|
|
|
quitChan chan struct{}
|
2018-05-15 21:16:49 +00:00
|
|
|
cmd *exec.Cmd
|
|
|
|
cmdIn io.WriteCloser
|
|
|
|
cmdOutBuf []byte
|
|
|
|
cmdHistory []cmdItem
|
2018-07-11 17:49:55 +00:00
|
|
|
cmdHistoryBeg int
|
2018-05-15 21:16:49 +00:00
|
|
|
cmdHistoryInd int
|
2016-10-27 19:24:42 +00:00
|
|
|
}
|
|
|
|
|
2021-02-21 14:48:23 +00:00
|
|
|
func newApp(ui *ui, nav *nav) *app {
|
2020-12-27 01:23:03 +00:00
|
|
|
quitChan := make(chan struct{}, 1)
|
2020-04-13 16:32:42 +00:00
|
|
|
|
2020-10-20 21:31:04 +00:00
|
|
|
app := &app{
|
2017-11-19 18:55:13 +00:00
|
|
|
ui: ui,
|
|
|
|
nav: nav,
|
2018-06-09 19:02:09 +00:00
|
|
|
ticker: new(time.Ticker),
|
2020-04-13 16:32:42 +00:00
|
|
|
quitChan: quitChan,
|
2016-10-27 19:24:42 +00:00
|
|
|
}
|
2020-10-20 21:31:04 +00:00
|
|
|
|
|
|
|
sigChan := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
|
|
switch <-sigChan {
|
|
|
|
case os.Interrupt:
|
|
|
|
return
|
|
|
|
case syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM:
|
|
|
|
app.writeHistory()
|
|
|
|
os.Remove(gLogPath)
|
|
|
|
os.Exit(3)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return app
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
func (app *app) readFile(path string) {
|
|
|
|
log.Printf("reading file: %s", path)
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
2019-02-28 18:58:14 +00:00
|
|
|
app.ui.echoerrf("opening file: %s", err)
|
2017-11-19 18:55:13 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
defer f.Close()
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
p := newParser(f)
|
2018-05-20 17:30:41 +00:00
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
for p.parse() {
|
|
|
|
p.expr.eval(app, nil)
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
if p.err != nil {
|
2019-02-28 18:58:14 +00:00
|
|
|
app.ui.echoerrf("%s", p.err)
|
2017-11-19 18:55:13 +00:00
|
|
|
}
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2018-07-11 17:49:55 +00:00
|
|
|
func (app *app) readHistory() error {
|
|
|
|
f, err := os.Open(gHistoryPath)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("opening history file: %s", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
|
|
for scanner.Scan() {
|
|
|
|
toks := strings.SplitN(scanner.Text(), " ", 2)
|
2020-09-01 20:57:40 +00:00
|
|
|
if toks[0] != ":" && toks[0] != "$" && toks[0] != "%" && toks[0] != "!" && toks[0] != "&" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if len(toks) < 2 {
|
|
|
|
continue
|
|
|
|
}
|
2018-07-11 17:49:55 +00:00
|
|
|
app.cmdHistory = append(app.cmdHistory, cmdItem{toks[0], toks[1]})
|
|
|
|
}
|
|
|
|
|
|
|
|
app.cmdHistoryBeg = len(app.cmdHistory)
|
|
|
|
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
|
|
return fmt.Errorf("reading history file: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (app *app) writeHistory() error {
|
|
|
|
if len(app.cmdHistory) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
local := make([]cmdItem, len(app.cmdHistory)-app.cmdHistoryBeg)
|
|
|
|
copy(local, app.cmdHistory[app.cmdHistoryBeg:])
|
|
|
|
app.cmdHistory = nil
|
|
|
|
|
|
|
|
if err := app.readHistory(); err != nil {
|
|
|
|
return fmt.Errorf("reading history file: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
app.cmdHistory = append(app.cmdHistory, local...)
|
|
|
|
|
|
|
|
if err := os.MkdirAll(filepath.Dir(gHistoryPath), os.ModePerm); err != nil {
|
|
|
|
return fmt.Errorf("creating data directory: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Create(gHistoryPath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("creating history file: %s", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
if len(app.cmdHistory) > 1000 {
|
|
|
|
app.cmdHistory = app.cmdHistory[len(app.cmdHistory)-1000:]
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, cmd := range app.cmdHistory {
|
|
|
|
_, err = f.WriteString(fmt.Sprintf("%s %s\n", cmd.prefix, cmd.value))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("writing history file: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-05-20 17:30:41 +00:00
|
|
|
// This is the main event loop of the application. Expressions are read from
|
|
|
|
// the client and the server on separate goroutines and sent here over channels
|
|
|
|
// for evaluation. Similarly directories and regular files are also read in
|
|
|
|
// separate goroutines and sent here for update.
|
2017-11-19 18:55:13 +00:00
|
|
|
func (app *app) loop() {
|
2021-02-21 14:48:23 +00:00
|
|
|
go app.nav.previewLoop(app.ui)
|
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
serverChan := readExpr()
|
2016-10-29 23:20:35 +00:00
|
|
|
|
2020-09-07 10:19:19 +00:00
|
|
|
app.ui.readExpr()
|
|
|
|
|
2019-02-10 16:48:49 +00:00
|
|
|
if gSelect != "" {
|
|
|
|
go func() {
|
2020-07-19 23:15:30 +00:00
|
|
|
lstat, err := os.Lstat(gSelect)
|
2019-05-18 14:51:32 +00:00
|
|
|
if err != nil {
|
|
|
|
app.ui.exprChan <- &callExpr{"echoerr", []string{err.Error()}, 1}
|
2020-07-19 23:15:30 +00:00
|
|
|
} else if lstat.IsDir() {
|
2019-05-18 14:51:32 +00:00
|
|
|
app.ui.exprChan <- &callExpr{"cd", []string{gSelect}, 1}
|
|
|
|
} else {
|
|
|
|
app.ui.exprChan <- &callExpr{"select", []string{gSelect}, 1}
|
|
|
|
}
|
2019-02-10 16:48:49 +00:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2019-02-08 21:55:11 +00:00
|
|
|
for _, path := range gConfigPaths {
|
|
|
|
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
|
|
|
app.readFile(path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-06 22:10:54 +00:00
|
|
|
for _, cmd := range gCommands {
|
|
|
|
p := newParser(strings.NewReader(cmd))
|
2019-02-08 16:54:04 +00:00
|
|
|
|
|
|
|
for p.parse() {
|
|
|
|
p.expr.eval(app, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.err != nil {
|
2019-02-28 18:58:14 +00:00
|
|
|
app.ui.echoerrf("%s", p.err)
|
2019-02-06 11:56:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-13 12:49:04 +00:00
|
|
|
for {
|
2016-10-27 19:24:42 +00:00
|
|
|
select {
|
2017-11-19 18:55:13 +00:00
|
|
|
case <-app.quitChan:
|
2019-02-28 19:10:57 +00:00
|
|
|
if app.nav.copyTotal > 0 {
|
|
|
|
app.ui.echoerr("quit: copy operation in progress")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-03-01 14:45:47 +00:00
|
|
|
if app.nav.moveTotal > 0 {
|
|
|
|
app.ui.echoerr("quit: move operation in progress")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-12-02 16:50:13 +00:00
|
|
|
if app.nav.deleteTotal > 0 {
|
2019-12-02 11:27:58 +00:00
|
|
|
app.ui.echoerr("quit: delete operation in progress")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-12-27 01:23:03 +00:00
|
|
|
app.nav.previewChan <- ""
|
2020-12-24 13:13:20 +00:00
|
|
|
|
2016-08-13 12:49:04 +00:00
|
|
|
log.Print("bye!")
|
2016-08-14 12:45:24 +00:00
|
|
|
|
2018-07-11 17:49:55 +00:00
|
|
|
if err := app.writeHistory(); err != nil {
|
|
|
|
log.Printf("writing history file: %s", err)
|
|
|
|
}
|
|
|
|
|
2016-08-14 12:45:24 +00:00
|
|
|
if gLastDirPath != "" {
|
|
|
|
f, err := os.Create(gLastDirPath)
|
|
|
|
if err != nil {
|
2016-08-17 19:09:34 +00:00
|
|
|
log.Printf("opening last dir file: %s", err)
|
2016-08-14 12:45:24 +00:00
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
2020-07-19 23:47:33 +00:00
|
|
|
_, err = f.WriteString(app.nav.currDir().path)
|
2016-08-14 12:45:24 +00:00
|
|
|
if err != nil {
|
2016-08-17 19:09:34 +00:00
|
|
|
log.Printf("writing last dir file: %s", err)
|
2016-08-14 12:45:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-13 12:49:04 +00:00
|
|
|
return
|
2019-02-28 18:04:38 +00:00
|
|
|
case n := <-app.nav.copyBytesChan:
|
2019-03-01 14:44:50 +00:00
|
|
|
app.nav.copyBytes += n
|
2019-03-01 01:01:33 +00:00
|
|
|
// n is usually 4096B so update roughly per 4096B x 1024 = 4MB copied
|
2019-02-28 18:04:38 +00:00
|
|
|
if app.nav.copyUpdate++; app.nav.copyUpdate >= 1024 {
|
|
|
|
app.nav.copyUpdate = 0
|
|
|
|
app.ui.draw(app.nav)
|
|
|
|
}
|
|
|
|
case n := <-app.nav.copyTotalChan:
|
|
|
|
app.nav.copyTotal += n
|
|
|
|
if n < 0 {
|
|
|
|
app.nav.copyBytes += n
|
|
|
|
}
|
2019-03-01 14:44:50 +00:00
|
|
|
if app.nav.copyTotal == 0 {
|
|
|
|
app.nav.copyUpdate = 0
|
|
|
|
}
|
|
|
|
app.ui.draw(app.nav)
|
|
|
|
case n := <-app.nav.moveCountChan:
|
|
|
|
app.nav.moveCount += n
|
|
|
|
if app.nav.moveUpdate++; app.nav.moveUpdate >= 1000 {
|
|
|
|
app.nav.moveUpdate = 0
|
|
|
|
app.ui.draw(app.nav)
|
|
|
|
}
|
|
|
|
case n := <-app.nav.moveTotalChan:
|
|
|
|
app.nav.moveTotal += n
|
|
|
|
if n < 0 {
|
|
|
|
app.nav.moveCount += n
|
|
|
|
}
|
|
|
|
if app.nav.moveTotal == 0 {
|
|
|
|
app.nav.moveUpdate = 0
|
|
|
|
}
|
2019-02-28 18:04:38 +00:00
|
|
|
app.ui.draw(app.nav)
|
2019-12-02 16:50:13 +00:00
|
|
|
case n := <-app.nav.deleteCountChan:
|
|
|
|
app.nav.deleteCount += n
|
|
|
|
if app.nav.deleteUpdate++; app.nav.deleteUpdate >= 1000 {
|
|
|
|
app.nav.deleteUpdate = 0
|
|
|
|
app.ui.draw(app.nav)
|
|
|
|
}
|
|
|
|
case n := <-app.nav.deleteTotalChan:
|
|
|
|
app.nav.deleteTotal += n
|
|
|
|
if n < 0 {
|
|
|
|
app.nav.deleteCount += n
|
|
|
|
}
|
|
|
|
if app.nav.deleteTotal == 0 {
|
|
|
|
app.nav.deleteUpdate = 0
|
|
|
|
}
|
|
|
|
app.ui.draw(app.nav)
|
2018-01-11 16:25:48 +00:00
|
|
|
case d := <-app.nav.dirChan:
|
2020-07-16 20:13:40 +00:00
|
|
|
app.nav.checkDir(d)
|
|
|
|
|
2018-01-26 21:28:07 +00:00
|
|
|
prev, ok := app.nav.dirCache[d.path]
|
|
|
|
if ok {
|
2018-08-27 15:57:55 +00:00
|
|
|
d.ind = prev.ind
|
2018-08-22 17:05:22 +00:00
|
|
|
d.sel(prev.name(), app.nav.height)
|
2018-01-26 21:28:07 +00:00
|
|
|
}
|
|
|
|
|
2018-01-11 16:25:48 +00:00
|
|
|
app.nav.dirCache[d.path] = d
|
|
|
|
|
|
|
|
for i := range app.nav.dirs {
|
|
|
|
if app.nav.dirs[i].path == d.path {
|
|
|
|
app.nav.dirs[i] = d
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
app.nav.position()
|
|
|
|
|
|
|
|
curr, err := app.nav.currFile()
|
|
|
|
if err == nil {
|
|
|
|
if d.path == app.nav.currDir().path {
|
2021-01-04 14:01:29 +00:00
|
|
|
app.ui.loadFile(app.nav, true)
|
2018-01-11 16:25:48 +00:00
|
|
|
}
|
|
|
|
if d.path == curr.path {
|
|
|
|
app.ui.dirPrev = d
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-28 17:13:28 +00:00
|
|
|
app.ui.draw(app.nav)
|
2018-02-10 15:59:19 +00:00
|
|
|
case r := <-app.nav.regChan:
|
2020-12-27 01:23:03 +00:00
|
|
|
app.nav.checkReg(r)
|
2020-07-16 20:13:40 +00:00
|
|
|
|
2018-02-10 15:59:19 +00:00
|
|
|
app.nav.regCache[r.path] = r
|
|
|
|
|
2018-01-28 17:13:28 +00:00
|
|
|
curr, err := app.nav.currFile()
|
|
|
|
if err == nil {
|
2018-02-10 15:59:19 +00:00
|
|
|
if r.path == curr.path {
|
|
|
|
app.ui.regPrev = r
|
2018-01-28 17:13:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-11 16:25:48 +00:00
|
|
|
app.ui.draw(app.nav)
|
2020-09-07 10:19:19 +00:00
|
|
|
case ev := <-app.ui.evChan:
|
|
|
|
e := app.ui.readEvent(ev)
|
|
|
|
if e == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
e.eval(app, nil)
|
|
|
|
loop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case ev := <-app.ui.evChan:
|
|
|
|
e = app.ui.readEvent(ev)
|
|
|
|
if e == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
e.eval(app, nil)
|
|
|
|
default:
|
|
|
|
break loop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
app.ui.draw(app.nav)
|
|
|
|
case e := <-app.ui.exprChan:
|
2018-04-12 15:04:37 +00:00
|
|
|
e.eval(app, nil)
|
2016-10-27 19:24:42 +00:00
|
|
|
app.ui.draw(app.nav)
|
2016-10-29 23:20:35 +00:00
|
|
|
case e := <-serverChan:
|
|
|
|
e.eval(app, nil)
|
|
|
|
app.ui.draw(app.nav)
|
2018-06-09 19:02:09 +00:00
|
|
|
case <-app.ticker.C:
|
|
|
|
app.nav.renew()
|
2021-01-04 14:01:29 +00:00
|
|
|
app.ui.loadFile(app.nav, false)
|
2018-06-09 19:02:09 +00:00
|
|
|
app.ui.draw(app.nav)
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 16:52:34 +00:00
|
|
|
func (app *app) exportFiles() {
|
2019-03-17 17:16:19 +00:00
|
|
|
var currFile string
|
2020-07-19 23:47:33 +00:00
|
|
|
if curr, err := app.nav.currFile(); err == nil {
|
|
|
|
currFile = curr.path
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2019-03-17 17:16:19 +00:00
|
|
|
currSelections := app.nav.currSelections()
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2019-03-17 17:16:19 +00:00
|
|
|
exportFiles(currFile, currSelections)
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2018-05-20 17:30:41 +00:00
|
|
|
// This function is used to run a shell command. Modes are as follows:
|
2016-08-13 12:49:04 +00:00
|
|
|
//
|
2018-05-20 17:30:41 +00:00
|
|
|
// Prefix Wait Async Stdin Stdout Stderr UI action
|
|
|
|
// $ No No Yes Yes Yes Pause and then resume
|
|
|
|
// % No No Yes Yes Yes Statline for input/output
|
|
|
|
// ! Yes No Yes Yes Yes Pause and then resume
|
|
|
|
// & No Yes No No No Do nothing
|
2018-03-26 18:22:18 +00:00
|
|
|
func (app *app) runShell(s string, args []string, prefix string) {
|
2019-03-17 16:52:34 +00:00
|
|
|
app.exportFiles()
|
2020-09-17 21:50:45 +00:00
|
|
|
exportOpts()
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2017-08-05 16:23:55 +00:00
|
|
|
cmd := shellCommand(s, args)
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2018-03-26 18:22:18 +00:00
|
|
|
var out io.Reader
|
2019-10-07 16:08:39 +00:00
|
|
|
var err error
|
2018-03-26 18:22:18 +00:00
|
|
|
switch prefix {
|
|
|
|
case "$", "!":
|
2016-08-13 12:49:04 +00:00
|
|
|
cmd.Stdin = os.Stdin
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
2020-12-27 01:23:03 +00:00
|
|
|
app.nav.previewChan <- ""
|
2021-02-21 14:34:38 +00:00
|
|
|
app.ui.suspend()
|
2016-08-13 12:49:04 +00:00
|
|
|
defer app.ui.resume()
|
2018-04-15 15:18:39 +00:00
|
|
|
defer app.nav.renew()
|
2019-10-07 16:08:39 +00:00
|
|
|
|
|
|
|
err = cmd.Run()
|
2018-03-26 18:22:18 +00:00
|
|
|
case "%":
|
2018-04-03 19:22:58 +00:00
|
|
|
stdin, err := cmd.StdinPipe()
|
2018-03-26 18:22:18 +00:00
|
|
|
if err != nil {
|
2018-04-03 19:22:58 +00:00
|
|
|
log.Printf("writing stdin: %s", err)
|
2018-03-26 18:22:18 +00:00
|
|
|
}
|
2018-04-03 19:22:58 +00:00
|
|
|
app.cmdIn = stdin
|
|
|
|
stdout, err := cmd.StdoutPipe()
|
2018-03-26 18:22:18 +00:00
|
|
|
if err != nil {
|
2018-04-03 19:22:58 +00:00
|
|
|
log.Printf("reading stdout: %s", err)
|
2018-03-26 18:22:18 +00:00
|
|
|
}
|
2018-04-03 19:22:58 +00:00
|
|
|
out = stdout
|
|
|
|
cmd.Stderr = cmd.Stdout
|
2019-10-07 16:08:39 +00:00
|
|
|
fallthrough
|
|
|
|
case "&":
|
2018-03-26 18:22:18 +00:00
|
|
|
err = cmd.Start()
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
2019-02-28 18:58:14 +00:00
|
|
|
app.ui.echoerrf("running shell: %s", err)
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2018-03-26 18:22:18 +00:00
|
|
|
switch prefix {
|
|
|
|
case "!":
|
2021-02-21 15:01:20 +00:00
|
|
|
anyKey()
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
2017-04-09 15:18:02 +00:00
|
|
|
|
2021-01-04 14:01:29 +00:00
|
|
|
app.ui.loadFile(app.nav, true)
|
2018-03-26 18:22:18 +00:00
|
|
|
|
|
|
|
switch prefix {
|
|
|
|
case "%":
|
2019-02-08 21:55:11 +00:00
|
|
|
app.cmd = cmd
|
|
|
|
app.cmdOutBuf = nil
|
|
|
|
app.ui.msg = ""
|
|
|
|
app.ui.cmdPrefix = ">"
|
2019-02-08 21:48:23 +00:00
|
|
|
|
2019-02-08 21:55:11 +00:00
|
|
|
go func() {
|
2020-11-22 21:13:31 +00:00
|
|
|
eol := false
|
2019-02-08 21:48:23 +00:00
|
|
|
reader := bufio.NewReader(out)
|
2018-11-08 17:00:54 +00:00
|
|
|
for {
|
2018-12-03 12:43:12 +00:00
|
|
|
b, err := reader.ReadByte()
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
}
|
2020-11-22 21:13:31 +00:00
|
|
|
if eol {
|
|
|
|
eol = false
|
|
|
|
app.cmdOutBuf = nil
|
|
|
|
}
|
2019-02-08 21:48:23 +00:00
|
|
|
app.cmdOutBuf = append(app.cmdOutBuf, b)
|
|
|
|
if b == '\n' || b == '\r' {
|
2020-11-22 21:13:31 +00:00
|
|
|
eol = true
|
|
|
|
}
|
|
|
|
if reader.Buffered() > 0 {
|
|
|
|
continue
|
2018-04-03 19:22:58 +00:00
|
|
|
}
|
2020-11-22 21:13:31 +00:00
|
|
|
app.ui.exprChan <- &callExpr{"echo", []string{string(app.cmdOutBuf)}, 1}
|
2018-04-03 19:22:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := cmd.Wait(); err != nil {
|
|
|
|
log.Printf("running shell: %s", err)
|
|
|
|
}
|
2018-04-03 19:56:38 +00:00
|
|
|
app.cmd = nil
|
2018-04-03 19:22:58 +00:00
|
|
|
app.ui.cmdPrefix = ""
|
2018-08-27 16:01:21 +00:00
|
|
|
app.ui.exprChan <- &callExpr{"load", nil, 1}
|
2018-04-03 19:22:58 +00:00
|
|
|
}()
|
2020-07-14 16:44:13 +00:00
|
|
|
case "&":
|
|
|
|
go func() {
|
|
|
|
if err := cmd.Wait(); err != nil {
|
|
|
|
log.Printf("running shell: %s", err)
|
|
|
|
}
|
|
|
|
}()
|
2018-03-26 18:22:18 +00:00
|
|
|
}
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|