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