write.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. package door
  2. import (
  3. "bytes"
  4. "fmt"
  5. "log"
  6. "regexp"
  7. "strconv"
  8. "strings"
  9. )
  10. var FindANSIColor *regexp.Regexp
  11. const WRITE_DEBUG bool = false
  12. func init() {
  13. FindANSIColor = regexp.MustCompile("\x1b\\[([0-9;]*)m")
  14. }
  15. /*
  16. find \x1b[0;1;37;40m codes.
  17. return array of start/end positions in the given string.
  18. */
  19. func find_ansicolor(text string) [][]int {
  20. var color_codes [][]int
  21. // word, _ := regexp.Compile("\x1b\\[([0-9;]+)m")
  22. var colors [][]int = FindANSIColor.FindAllStringIndex(text, -1)
  23. // regexp seems to be ignoring the capture groups.
  24. // colors := word.FindAllSubmatchIndex([]byte(text), len(text))
  25. for _, pos := range colors {
  26. var txt string = text[pos[0]+2 : pos[1]-1]
  27. if txt == "" {
  28. txt = "0"
  29. }
  30. // log.Printf("Text: [%s]\n", txt)
  31. var codes []string = strings.Split(txt, ";")
  32. // log.Printf("Codes: [%#v]\n", codes)
  33. var code []int = make([]int, len(codes))
  34. for idx, c := range codes {
  35. var err error
  36. code[idx], err = strconv.Atoi(c)
  37. if err != nil {
  38. log.Printf("Atoi: %#v [%s]\n", err, c)
  39. }
  40. color_codes = append(color_codes, code)
  41. }
  42. }
  43. return color_codes
  44. }
  45. type ANSIColorParts struct {
  46. Bold bool
  47. Blink bool
  48. FG int
  49. BG int
  50. }
  51. // Parse an array of codes into their ANSIColorParts.
  52. func ParseColorArray(colorarray []int) ANSIColorParts {
  53. var acp ANSIColorParts
  54. acp.FG = -1
  55. acp.BG = -1
  56. if len(colorarray) == 0 {
  57. colorarray = []int{0}
  58. }
  59. for _, c := range colorarray {
  60. switch c {
  61. case 0:
  62. acp.FG = 7
  63. acp.BG = 0
  64. acp.Bold = false
  65. acp.Blink = false
  66. case 1:
  67. acp.Bold = true
  68. case 5:
  69. acp.Blink = true
  70. case 30, 31, 32, 33, 34, 35, 36, 37:
  71. acp.FG = c - 30
  72. case 40, 41, 42, 43, 44, 45, 46, 47:
  73. acp.BG = c - 40
  74. }
  75. }
  76. return acp
  77. }
  78. // Update the LastColor with the latest codes.
  79. func (d *Door) UpdateLastColor(output string, LastColor *[]int) {
  80. // use the last color information + whatever is in the string to
  81. // track the last color set
  82. var updated ANSIColorParts = ParseColorArray(*LastColor)
  83. var colors [][]int = find_ansicolor(output)
  84. for _, codes := range colors {
  85. if codes[0] == 0 {
  86. updated = ParseColorArray(codes)
  87. } else {
  88. newCode := ParseColorArray(codes)
  89. if newCode.Bold {
  90. updated.Bold = true
  91. }
  92. if newCode.Blink {
  93. updated.Blink = true
  94. }
  95. if (newCode.FG != -1) && (newCode.FG != updated.FG) {
  96. updated.FG = newCode.FG
  97. }
  98. if (newCode.BG != -1) && (newCode.BG != updated.BG) {
  99. updated.BG = newCode.BG
  100. }
  101. }
  102. }
  103. *LastColor = make([]int, 1)
  104. (*LastColor)[0] = 0
  105. if updated.Blink {
  106. *LastColor = append(*LastColor, 5)
  107. }
  108. if updated.Bold {
  109. *LastColor = append(*LastColor, 1)
  110. }
  111. if updated.FG != -1 {
  112. *LastColor = append(*LastColor, updated.FG+30)
  113. }
  114. if updated.BG != -1 {
  115. *LastColor = append(*LastColor, updated.BG+40)
  116. }
  117. }
  118. /*
  119. reading from a closed channel is easy to detect.
  120. res, ok := <-channel
  121. ok == false
  122. writing to a closed channel is a panic.
  123. Have the Write manage itself.
  124. */
  125. // For now, update is SavePos + output + RestorePos.
  126. // FUTURE: Access the screen struct to "save" Color+Attr+Pos.
  127. func (d *Door) Update(output []byte) {
  128. // sync.Pool ?
  129. d.WriteA(SAVE_POS, output, RESTORE_POS)
  130. /*
  131. var buffer bytes.Buffer
  132. buffer.WriteString(SAVE_POS)
  133. buffer.Write(output)
  134. buffer.WriteString(RESTORE_POS)
  135. d.Writer.Write(buffer.Bytes())
  136. */
  137. // d.Write(SAVE_POS + output + RESTORE_POS)
  138. }
  139. func (d *Door) WriteS(output string) {
  140. d.Write([]byte(output))
  141. }
  142. func (d *Door) WriteA(a ...interface{}) {
  143. d.writeMutex.Lock()
  144. defer d.writeMutex.Unlock()
  145. for _, item := range a {
  146. switch item.(type) {
  147. case string:
  148. d.LockedWrite([]byte(item.(string)))
  149. case []byte:
  150. d.LockedWrite(item.([]byte))
  151. case *bytes.Buffer:
  152. d.LockedWrite(item.(*bytes.Buffer).Bytes())
  153. default:
  154. log.Printf("Unknown/unsupported type: %T\n", item)
  155. }
  156. }
  157. }
  158. // Is this byte the end of the ANSI CSI?
  159. func EndCSI(c byte) bool {
  160. return (c >= 0x40) && (c <= 0x7f)
  161. }
  162. func (d *Door) ANSIProcess() {
  163. var csi byte = d.ansiCode[len(d.ansiCode)-1]
  164. switch csi {
  165. case 's':
  166. // Save Color
  167. var slen int = len(d.LastColor)
  168. if cap(d.Writer.LastSavedColor) < slen {
  169. d.Writer.LastSavedColor = make([]int, slen)
  170. } else {
  171. d.Writer.LastSavedColor = d.Writer.LastSavedColor[0:slen]
  172. }
  173. copy(d.Writer.LastSavedColor, d.LastColor)
  174. if WRITE_DEBUG {
  175. log.Printf("ColorSave: %d\n", d.Writer.LastSavedColor)
  176. }
  177. case 'u':
  178. // Restore Color
  179. var slen int = len(d.Writer.LastSavedColor)
  180. if cap(d.LastColor) < slen {
  181. d.LastColor = make([]int, slen)
  182. } else {
  183. d.LastColor = d.LastColor[0:slen]
  184. }
  185. copy(d.LastColor, d.Writer.LastSavedColor)
  186. if WRITE_DEBUG {
  187. log.Printf("ColorRestore: %d\n", d.LastColor)
  188. }
  189. case 'm':
  190. // Process color code
  191. var color [5]int
  192. var cpos int
  193. var code int
  194. var pos int
  195. for pos = 0; pos < len(d.ansiCode); pos++ {
  196. var b byte = d.ansiCode[pos]
  197. switch b {
  198. case ';':
  199. color[cpos] = code
  200. code = 0
  201. cpos++
  202. case '1', '2', '3', '4', '5', '6', '7', '8', '9', '0':
  203. code *= 10
  204. code += int(b - '0')
  205. }
  206. }
  207. if code != 0 {
  208. color[cpos] = code
  209. code = 0
  210. cpos++
  211. }
  212. // Make sure there is always at least one code here.
  213. if cpos == 0 {
  214. color[cpos] = 0
  215. cpos++
  216. }
  217. if WRITE_DEBUG {
  218. log.Printf("color: [%d]", color[0:cpos])
  219. }
  220. var newColor = ParseColorArray(d.LastColor)
  221. for _, c := range color[0:cpos] {
  222. switch c {
  223. case 0:
  224. newColor.FG = 7
  225. newColor.BG = 0
  226. newColor.Bold = false
  227. newColor.Blink = false
  228. case 1:
  229. newColor.Bold = true
  230. case 5:
  231. newColor.Blink = true
  232. case 30, 31, 32, 33, 34, 35, 36, 37:
  233. newColor.FG = c - 30
  234. case 40, 41, 42, 43, 44, 45, 46, 47:
  235. newColor.BG = c - 40
  236. }
  237. }
  238. // Reset LastColor
  239. d.LastColor = d.LastColor[0:0]
  240. d.LastColor = append(d.LastColor, 0)
  241. if newColor.Blink {
  242. d.LastColor = append(d.LastColor, 5)
  243. }
  244. if newColor.Bold {
  245. d.LastColor = append(d.LastColor, 1)
  246. }
  247. if newColor.FG != -1 {
  248. d.LastColor = append(d.LastColor, newColor.FG+30)
  249. }
  250. if newColor.BG != -1 {
  251. d.LastColor = append(d.LastColor, newColor.BG+40)
  252. }
  253. if WRITE_DEBUG {
  254. log.Printf("LastColor: [%d]", d.LastColor)
  255. }
  256. }
  257. if WRITE_DEBUG {
  258. var outputByte [20]byte
  259. var output []byte = outputByte[0:0]
  260. output = fmt.Appendf(output, "ANSI: [%q]\n", d.ansiCode)
  261. log.Printf("%s", output)
  262. }
  263. // Reset
  264. d.ansiCode = d.ansiCode[0:0]
  265. d.ansiCSI = false
  266. }
  267. func (d *Door) ANSIScan(output []byte) {
  268. var pos, nextpos int
  269. var olen int = len(output)
  270. var c byte
  271. if d.ansiCSI {
  272. for pos < olen {
  273. c = output[pos]
  274. d.ansiCode = append(d.ansiCode, c)
  275. pos++
  276. if EndCSI(c) {
  277. d.ANSIProcess()
  278. break
  279. }
  280. }
  281. if pos == olen {
  282. return
  283. // pos == -1 // end
  284. }
  285. }
  286. for pos != -1 {
  287. nextpos = bytes.Index(output[pos:], []byte{'\x1b'})
  288. if nextpos != -1 {
  289. // Found ESC
  290. nextpos += pos + 1
  291. if output[nextpos] == '[' {
  292. d.ansiCSI = true
  293. nextpos++
  294. for nextpos < olen {
  295. c = output[nextpos]
  296. d.ansiCode = append(d.ansiCode, c)
  297. nextpos++
  298. if EndCSI(c) {
  299. d.ANSIProcess()
  300. break
  301. }
  302. }
  303. }
  304. pos = nextpos
  305. } else {
  306. return
  307. }
  308. }
  309. }
  310. func (d *Door) NewLinesTranslate(output []byte) []byte {
  311. var pos, nextpos int
  312. if d.nlBuffer == nil {
  313. d.nlBuffer = &bytes.Buffer{}
  314. }
  315. d.nlBuffer.Reset()
  316. for pos != -1 {
  317. nextpos = bytes.Index(output[pos:], []byte{'\n'})
  318. if nextpos != -1 {
  319. nextpos += pos
  320. // Something to do
  321. d.nlBuffer.Write(output[pos:nextpos])
  322. nextpos++
  323. pos = nextpos
  324. d.nlBuffer.Write([]byte("\r\n"))
  325. } else {
  326. d.nlBuffer.Write(output[pos:])
  327. pos = nextpos // -1
  328. }
  329. }
  330. // log.Printf(">> %q\n", ow.nlBuffer.Bytes())
  331. return d.nlBuffer.Bytes()
  332. }
  333. func (d *Door) LockedWrite(output []byte) {
  334. if d.writeMutex.TryLock() {
  335. log.Panic("LockedWrite: mutex was NOT locked.")
  336. }
  337. /*
  338. if bytes.HasSuffix(output, []byte(RestorePos)) {
  339. // Write the current color
  340. lastColor = Color(d.LastColor)
  341. //fmt.Append(lastColor, []byte(Color(d.LastColor)))
  342. log.Printf("Restore LastColor: %d => %q", d.LastColor, lastColor)
  343. }
  344. */
  345. if true {
  346. output = d.NewLinesTranslate(output)
  347. }
  348. if WRITE_DEBUG {
  349. log.Printf(">> %q\n", output)
  350. }
  351. d.Writer.Write(output)
  352. d.ANSIScan(output)
  353. if bytes.HasSuffix(output, []byte(RestorePos)) {
  354. lastColor := Color(d.LastColor)
  355. //fmt.Append(lastColor, []byte(Color(d.LastColor)))
  356. if WRITE_DEBUG {
  357. log.Printf("Restore LastColor: %d => %q", d.LastColor, lastColor)
  358. }
  359. d.Writer.Write([]byte(lastColor))
  360. //d.ANSIScan([]byte(lastColor))
  361. }
  362. /*
  363. if len(lastColor) != 0 {
  364. log.Println("Restored HERE...")
  365. d.Writer.Write([]byte(lastColor))
  366. d.ANSIScan([]byte(lastColor))
  367. }
  368. */
  369. // else {
  370. // Only update if we're NOT restoring...
  371. // Update
  372. // d.ANSIScan(output)
  373. // }
  374. }
  375. func (d *Door) Write(output []byte) {
  376. if len(output) == 0 {
  377. return
  378. }
  379. d.writeMutex.Lock()
  380. defer d.writeMutex.Unlock()
  381. d.LockedWrite(output)
  382. }