gexpect.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. package gexpect
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "os"
  8. "os/exec"
  9. "regexp"
  10. "time"
  11. "unicode/utf8"
  12. shell "github.com/kballard/go-shellquote"
  13. "github.com/kr/pty"
  14. )
  15. type ExpectSubprocess struct {
  16. Cmd *exec.Cmd
  17. buf *buffer
  18. outputBuffer []byte
  19. }
  20. type buffer struct {
  21. f *os.File
  22. b bytes.Buffer
  23. collect bool
  24. collection bytes.Buffer
  25. }
  26. func (buf *buffer) StartCollecting() {
  27. buf.collect = true
  28. }
  29. func (buf *buffer) StopCollecting() (result string) {
  30. result = string(buf.collection.Bytes())
  31. buf.collect = false
  32. buf.collection.Reset()
  33. return result
  34. }
  35. func (buf *buffer) Read(chunk []byte) (int, error) {
  36. nread := 0
  37. if buf.b.Len() > 0 {
  38. n, err := buf.b.Read(chunk)
  39. if err != nil {
  40. return n, err
  41. }
  42. if n == len(chunk) {
  43. return n, nil
  44. }
  45. nread = n
  46. }
  47. fn, err := buf.f.Read(chunk[nread:])
  48. return fn + nread, err
  49. }
  50. func (buf *buffer) ReadRune() (r rune, size int, err error) {
  51. l := buf.b.Len()
  52. chunk := make([]byte, utf8.UTFMax)
  53. if l > 0 {
  54. n, err := buf.b.Read(chunk)
  55. if err != nil {
  56. return 0, 0, err
  57. }
  58. if utf8.FullRune(chunk) {
  59. r, rL := utf8.DecodeRune(chunk)
  60. if n > rL {
  61. buf.PutBack(chunk[rL:n])
  62. }
  63. if buf.collect {
  64. buf.collection.WriteRune(r)
  65. }
  66. return r, rL, nil
  67. }
  68. }
  69. // else add bytes from the file, then try that
  70. for l < utf8.UTFMax {
  71. fn, err := buf.f.Read(chunk[l : l+1])
  72. if err != nil {
  73. return 0, 0, err
  74. }
  75. l = l + fn
  76. if utf8.FullRune(chunk) {
  77. r, rL := utf8.DecodeRune(chunk)
  78. if buf.collect {
  79. buf.collection.WriteRune(r)
  80. }
  81. return r, rL, nil
  82. }
  83. }
  84. return 0, 0, errors.New("File is not a valid UTF=8 encoding")
  85. }
  86. func (buf *buffer) PutBack(chunk []byte) {
  87. if len(chunk) == 0 {
  88. return
  89. }
  90. if buf.b.Len() == 0 {
  91. buf.b.Write(chunk)
  92. return
  93. }
  94. d := make([]byte, 0, len(chunk)+buf.b.Len())
  95. d = append(d, chunk...)
  96. d = append(d, buf.b.Bytes()...)
  97. buf.b.Reset()
  98. buf.b.Write(d)
  99. }
  100. func SpawnAtDirectory(command string, directory string) (*ExpectSubprocess, error) {
  101. expect, err := _spawn(command)
  102. if err != nil {
  103. return nil, err
  104. }
  105. expect.Cmd.Dir = directory
  106. return _start(expect)
  107. }
  108. func Command(command string) (*ExpectSubprocess, error) {
  109. expect, err := _spawn(command)
  110. if err != nil {
  111. return nil, err
  112. }
  113. return expect, nil
  114. }
  115. func (expect *ExpectSubprocess) Start() error {
  116. _, err := _start(expect)
  117. return err
  118. }
  119. func Spawn(command string) (*ExpectSubprocess, error) {
  120. expect, err := _spawn(command)
  121. if err != nil {
  122. return nil, err
  123. }
  124. return _start(expect)
  125. }
  126. func (expect *ExpectSubprocess) Close() error {
  127. return expect.Cmd.Process.Kill()
  128. }
  129. func (expect *ExpectSubprocess) AsyncInteractChannels() (send chan string, receive chan string) {
  130. receive = make(chan string)
  131. send = make(chan string)
  132. go func() {
  133. for {
  134. str, err := expect.ReadLine()
  135. if err != nil {
  136. close(receive)
  137. return
  138. }
  139. receive <- str
  140. }
  141. }()
  142. go func() {
  143. for {
  144. select {
  145. case sendCommand, exists := <-send:
  146. {
  147. if !exists {
  148. return
  149. }
  150. err := expect.Send(sendCommand)
  151. if err != nil {
  152. receive <- "gexpect Error: " + err.Error()
  153. return
  154. }
  155. }
  156. }
  157. }
  158. }()
  159. return
  160. }
  161. func (expect *ExpectSubprocess) ExpectRegex(regex string) (bool, error) {
  162. return regexp.MatchReader(regex, expect.buf)
  163. }
  164. func (expect *ExpectSubprocess) expectRegexFind(regex string, output bool) ([]string, string, error) {
  165. re, err := regexp.Compile(regex)
  166. if err != nil {
  167. return nil, "", err
  168. }
  169. expect.buf.StartCollecting()
  170. pairs := re.FindReaderSubmatchIndex(expect.buf)
  171. stringIndexedInto := expect.buf.StopCollecting()
  172. l := len(pairs)
  173. numPairs := l / 2
  174. result := make([]string, numPairs)
  175. for i := 0; i < numPairs; i += 1 {
  176. result[i] = stringIndexedInto[pairs[i*2]:pairs[i*2+1]]
  177. }
  178. // convert indexes to strings
  179. if len(result) == 0 {
  180. err = fmt.Errorf("ExpectRegex didn't find regex '%v'.", regex)
  181. }
  182. return result, stringIndexedInto, err
  183. }
  184. func (expect *ExpectSubprocess) expectTimeoutRegexFind(regex string, timeout time.Duration) (result []string, out string, err error) {
  185. t := make(chan bool)
  186. go func() {
  187. result, out, err = expect.ExpectRegexFindWithOutput(regex)
  188. t <- false
  189. }()
  190. go func() {
  191. time.Sleep(timeout)
  192. err = fmt.Errorf("ExpectRegex timed out after %v finding '%v'.\nOutput:\n%s", timeout, regex, expect.Collect())
  193. t <- true
  194. }()
  195. <-t
  196. return result, out, err
  197. }
  198. func (expect *ExpectSubprocess) ExpectRegexFind(regex string) ([]string, error) {
  199. result, _, err := expect.expectRegexFind(regex, false)
  200. return result, err
  201. }
  202. func (expect *ExpectSubprocess) ExpectTimeoutRegexFind(regex string, timeout time.Duration) ([]string, error) {
  203. result, _, err := expect.expectTimeoutRegexFind(regex, timeout)
  204. return result, err
  205. }
  206. func (expect *ExpectSubprocess) ExpectRegexFindWithOutput(regex string) ([]string, string, error) {
  207. return expect.expectRegexFind(regex, true)
  208. }
  209. func (expect *ExpectSubprocess) ExpectTimeoutRegexFindWithOutput(regex string, timeout time.Duration) ([]string, string, error) {
  210. return expect.expectTimeoutRegexFind(regex, timeout)
  211. }
  212. func buildKMPTable(searchString string) []int {
  213. pos := 2
  214. cnd := 0
  215. length := len(searchString)
  216. var table []int
  217. if length < 2 {
  218. length = 2
  219. }
  220. table = make([]int, length)
  221. table[0] = -1
  222. table[1] = 0
  223. for pos < len(searchString) {
  224. if searchString[pos-1] == searchString[cnd] {
  225. cnd += 1
  226. table[pos] = cnd
  227. pos += 1
  228. } else if cnd > 0 {
  229. cnd = table[cnd]
  230. } else {
  231. table[pos] = 0
  232. pos += 1
  233. }
  234. }
  235. return table
  236. }
  237. func (expect *ExpectSubprocess) ExpectTimeout(searchString string, timeout time.Duration) (e error) {
  238. result := make(chan error)
  239. go func() {
  240. result <- expect.Expect(searchString)
  241. }()
  242. select {
  243. case e = <-result:
  244. case <-time.After(timeout):
  245. e = fmt.Errorf("Expect timed out after %v waiting for '%v'.\nOutput:\n%s", timeout, searchString, expect.Collect())
  246. }
  247. return e
  248. }
  249. func (expect *ExpectSubprocess) Expect(searchString string) (e error) {
  250. chunk := make([]byte, len(searchString)*2)
  251. target := len(searchString)
  252. if expect.outputBuffer != nil {
  253. expect.outputBuffer = expect.outputBuffer[0:]
  254. }
  255. m := 0
  256. i := 0
  257. // Build KMP Table
  258. table := buildKMPTable(searchString)
  259. for {
  260. n, err := expect.buf.Read(chunk)
  261. if err != nil {
  262. return err
  263. }
  264. if expect.outputBuffer != nil {
  265. expect.outputBuffer = append(expect.outputBuffer, chunk[:n]...)
  266. }
  267. offset := m + i
  268. for m+i-offset < n {
  269. if searchString[i] == chunk[m+i-offset] {
  270. i += 1
  271. if i == target {
  272. unreadIndex := m + i - offset
  273. if len(chunk) > unreadIndex {
  274. expect.buf.PutBack(chunk[unreadIndex:])
  275. }
  276. return nil
  277. }
  278. } else {
  279. m += i - table[i]
  280. if table[i] > -1 {
  281. i = table[i]
  282. } else {
  283. i = 0
  284. }
  285. }
  286. }
  287. }
  288. }
  289. func (expect *ExpectSubprocess) Send(command string) error {
  290. _, err := io.WriteString(expect.buf.f, command)
  291. return err
  292. }
  293. func (expect *ExpectSubprocess) Capture() {
  294. if expect.outputBuffer == nil {
  295. expect.outputBuffer = make([]byte, 0)
  296. }
  297. }
  298. func (expect *ExpectSubprocess) Collect() []byte {
  299. collectOutput := make([]byte, len(expect.outputBuffer))
  300. copy(collectOutput, expect.outputBuffer)
  301. expect.outputBuffer = nil
  302. return collectOutput
  303. }
  304. func (expect *ExpectSubprocess) SendLine(command string) error {
  305. _, err := io.WriteString(expect.buf.f, command+"\r\n")
  306. return err
  307. }
  308. func (expect *ExpectSubprocess) Interact() {
  309. defer expect.Cmd.Wait()
  310. io.Copy(os.Stdout, &expect.buf.b)
  311. go io.Copy(os.Stdout, expect.buf.f)
  312. go io.Copy(expect.buf.f, os.Stdin)
  313. }
  314. func (expect *ExpectSubprocess) ReadUntil(delim byte) ([]byte, error) {
  315. join := make([]byte, 1, 512)
  316. chunk := make([]byte, 255)
  317. for {
  318. n, err := expect.buf.Read(chunk)
  319. if err != nil {
  320. return join, err
  321. }
  322. for i := 0; i < n; i++ {
  323. if chunk[i] == delim {
  324. if len(chunk) > i+1 {
  325. expect.buf.PutBack(chunk[i+1:])
  326. }
  327. return join, nil
  328. } else {
  329. join = append(join, chunk[i])
  330. }
  331. }
  332. }
  333. }
  334. func (expect *ExpectSubprocess) Wait() error {
  335. return expect.Cmd.Wait()
  336. }
  337. func (expect *ExpectSubprocess) ReadLine() (string, error) {
  338. str, err := expect.ReadUntil('\n')
  339. if err != nil {
  340. return "", err
  341. }
  342. return string(str), nil
  343. }
  344. func _start(expect *ExpectSubprocess) (*ExpectSubprocess, error) {
  345. f, err := pty.Start(expect.Cmd)
  346. if err != nil {
  347. return nil, err
  348. }
  349. expect.buf.f = f
  350. return expect, nil
  351. }
  352. func _spawn(command string) (*ExpectSubprocess, error) {
  353. wrapper := new(ExpectSubprocess)
  354. wrapper.outputBuffer = nil
  355. splitArgs, err := shell.Split(command)
  356. if err != nil {
  357. return nil, err
  358. }
  359. numArguments := len(splitArgs) - 1
  360. if numArguments < 0 {
  361. return nil, errors.New("gexpect: No command given to spawn")
  362. }
  363. path, err := exec.LookPath(splitArgs[0])
  364. if err != nil {
  365. return nil, err
  366. }
  367. if numArguments >= 1 {
  368. wrapper.Cmd = exec.Command(path, splitArgs[1:]...)
  369. } else {
  370. wrapper.Cmd = exec.Command(path)
  371. }
  372. wrapper.buf = new(buffer)
  373. return wrapper, nil
  374. }