write.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package door
  2. import (
  3. "log"
  4. "regexp"
  5. "strconv"
  6. "strings"
  7. )
  8. var FindANSIColor *regexp.Regexp
  9. func init() {
  10. FindANSIColor = regexp.MustCompile("\x1b\\[([0-9;]*)m")
  11. }
  12. /*
  13. find \x1b[0;1;37;40m codes.
  14. return array of start/end positions in the given string.
  15. */
  16. func find_ansicolor(text string) [][]int {
  17. var color_codes [][]int
  18. // word, _ := regexp.Compile("\x1b\\[([0-9;]+)m")
  19. colors := FindANSIColor.FindAllStringIndex(text, -1)
  20. // regexp seems to be ignoring the capture groups.
  21. // colors := word.FindAllSubmatchIndex([]byte(text), len(text))
  22. for _, pos := range colors {
  23. txt := text[pos[0]+2 : pos[1]-1]
  24. if txt == "" {
  25. txt = "0"
  26. }
  27. // log.Printf("Text: [%s]\n", txt)
  28. codes := strings.Split(txt, ";")
  29. // log.Printf("Codes: [%#v]\n", codes)
  30. code := make([]int, len(codes))
  31. for idx, c := range codes {
  32. var err error
  33. code[idx], err = strconv.Atoi(c)
  34. if err != nil {
  35. log.Printf("Atoi: %#v [%s]\n", err, c)
  36. }
  37. color_codes = append(color_codes, code)
  38. }
  39. }
  40. return color_codes
  41. }
  42. type ANSIColorParts struct {
  43. Bold bool
  44. Blink bool
  45. FG int
  46. BG int
  47. }
  48. // Parse an array of codes into their ANSIColorParts.
  49. func ParseColorArray(colorarray []int) ANSIColorParts {
  50. var acp ANSIColorParts
  51. acp.FG = -1
  52. acp.BG = -1
  53. for _, c := range colorarray {
  54. switch c {
  55. case 0:
  56. acp.FG = 7
  57. acp.BG = 0
  58. case 1:
  59. acp.Bold = true
  60. case 5:
  61. acp.Blink = true
  62. case 30, 31, 32, 33, 34, 35, 36, 37:
  63. acp.FG = c - 30
  64. case 40, 41, 42, 43, 44, 45, 46, 47:
  65. acp.BG = c - 40
  66. }
  67. }
  68. return acp
  69. }
  70. // Update the LastColor with the latest codes.
  71. func (d *Door) UpdateLastColor(output string) {
  72. // use the last color information + whatever is in the string to
  73. // track the last color set
  74. updated := ParseColorArray(d.LastColor)
  75. colors := find_ansicolor(output)
  76. for _, codes := range colors {
  77. if codes[0] == 0 {
  78. updated = ParseColorArray(codes)
  79. } else {
  80. newCode := ParseColorArray(codes)
  81. if newCode.Bold {
  82. updated.Bold = true
  83. }
  84. if newCode.Blink {
  85. updated.Blink = true
  86. }
  87. if (newCode.FG != -1) && (newCode.FG != updated.FG) {
  88. updated.FG = newCode.FG
  89. }
  90. if (newCode.BG != -1) && (newCode.BG != updated.BG) {
  91. updated.BG = newCode.BG
  92. }
  93. }
  94. }
  95. d.LastColor = make([]int, 1)
  96. d.LastColor[0] = 0
  97. if updated.Blink {
  98. d.LastColor = append(d.LastColor, 5)
  99. }
  100. if updated.Bold {
  101. d.LastColor = append(d.LastColor, 1)
  102. }
  103. if updated.FG != -1 {
  104. d.LastColor = append(d.LastColor, updated.FG+30)
  105. }
  106. if updated.BG != -1 {
  107. d.LastColor = append(d.LastColor, updated.BG+40)
  108. }
  109. }
  110. /*
  111. reading from a closed channel is easy to detect.
  112. res, ok := <-channel
  113. ok == false
  114. writing to a closed channel is a panic.
  115. */
  116. var writerChannel chan string
  117. // Write string to client.
  118. func (d *Door) Write(output string) {
  119. if d.Disconnected {
  120. return
  121. }
  122. defer func() {
  123. if r := recover(); r != nil {
  124. log.Println("Write error/HANGUP.")
  125. d.Disconnected = true
  126. }
  127. }()
  128. // Updating some other part of the screen.
  129. // Use SavePos/RestorePos in the same string.
  130. // We'll append the LastColor to the string,
  131. // allowing the update to not mess up current color.
  132. if strings.HasSuffix(output, RestorePos) {
  133. output += Color(d.LastColor...)
  134. } else {
  135. d.UpdateLastColor(output)
  136. }
  137. /*
  138. // Output the string, translating the ESC code to ^[ for outputting.
  139. temp := strings.Replace(output, "\x1b", "^[", -1)
  140. log.Printf("Parse: [%s]\n", temp)
  141. */
  142. writerChannel <- output
  143. }