]> git.lizzy.rs Git - micro.git/blob - internal/shell/shell.go
Add VLoc, VLocFromLoc and LocFromVLoc
[micro.git] / internal / shell / shell.go
1 package shell
2
3 import (
4         "bytes"
5         "errors"
6         "fmt"
7         "io"
8         "os"
9         "os/exec"
10         "os/signal"
11         "strings"
12
13         shellquote "github.com/kballard/go-shellquote"
14         "github.com/zyedidia/micro/v2/internal/screen"
15 )
16
17 // ExecCommand executes a command using exec
18 // It returns any output/errors
19 func ExecCommand(name string, arg ...string) (string, error) {
20         var err error
21         cmd := exec.Command(name, arg...)
22         outputBytes := &bytes.Buffer{}
23         cmd.Stdout = outputBytes
24         cmd.Stderr = outputBytes
25         err = cmd.Start()
26         if err != nil {
27                 return "", err
28         }
29         err = cmd.Wait() // wait for command to finish
30         outstring := outputBytes.String()
31         return outstring, err
32 }
33
34 // RunCommand executes a shell command and returns the output/error
35 func RunCommand(input string) (string, error) {
36         args, err := shellquote.Split(input)
37         if err != nil {
38                 return "", err
39         }
40         if len(args) == 0 {
41                 return "", errors.New("No arguments")
42         }
43         inputCmd := args[0]
44
45         return ExecCommand(inputCmd, args[1:]...)
46 }
47
48 // RunBackgroundShell runs a shell command in the background
49 // It returns a function which will run the command and returns a string
50 // message result
51 func RunBackgroundShell(input string) (func() string, error) {
52         args, err := shellquote.Split(input)
53         if err != nil {
54                 return nil, err
55         }
56         if len(args) == 0 {
57                 return nil, errors.New("No arguments")
58         }
59         inputCmd := args[0]
60         return func() string {
61                 output, err := RunCommand(input)
62                 totalLines := strings.Split(output, "\n")
63
64                 str := output
65                 if len(totalLines) < 3 {
66                         if err == nil {
67                                 str = fmt.Sprint(inputCmd, " exited without error")
68                         } else {
69                                 str = fmt.Sprint(inputCmd, " exited with error: ", err, ": ", output)
70                         }
71                 }
72                 return str
73         }, nil
74 }
75
76 // RunInteractiveShell runs a shellcommand interactively
77 func RunInteractiveShell(input string, wait bool, getOutput bool) (string, error) {
78         args, err := shellquote.Split(input)
79         if err != nil {
80                 return "", err
81         }
82         if len(args) == 0 {
83                 return "", errors.New("No arguments")
84         }
85         inputCmd := args[0]
86
87         // Shut down the screen because we're going to interact directly with the shell
88         screenb := screen.TempFini()
89
90         args = args[1:]
91
92         // Set up everything for the command
93         outputBytes := &bytes.Buffer{}
94         cmd := exec.Command(inputCmd, args...)
95         cmd.Stdin = os.Stdin
96         if getOutput {
97                 cmd.Stdout = io.MultiWriter(os.Stdout, outputBytes)
98         } else {
99                 cmd.Stdout = os.Stdout
100         }
101         cmd.Stderr = os.Stderr
102
103         // This is a trap for Ctrl-C so that it doesn't kill micro
104         // Instead we trap Ctrl-C to kill the program we're running
105         c := make(chan os.Signal, 1)
106         signal.Notify(c, os.Interrupt)
107         go func() {
108                 for range c {
109                         cmd.Process.Kill()
110                 }
111         }()
112
113         cmd.Start()
114         err = cmd.Wait()
115
116         output := outputBytes.String()
117
118         if wait {
119                 // This is just so we don't return right away and let the user press enter to return
120                 screen.TermMessage("")
121         }
122
123         // Start the screen back up
124         screen.TempStart(screenb)
125
126         return output, err
127 }
128
129 // UserCommand runs the shell command
130 // The openTerm argument specifies whether a terminal should be opened (for viewing output
131 // or interacting with stdin)
132 // func UserCommand(input string, openTerm bool, waitToFinish bool) string {
133 //      if !openTerm {
134 //              RunBackgroundShell(input)
135 //              return ""
136 //      } else {
137 //              output, _ := RunInteractiveShell(input, waitToFinish, false)
138 //              return output
139 //      }
140 // }