You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

colors.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package log
  5. import (
  6. "fmt"
  7. "io"
  8. "reflect"
  9. "strconv"
  10. "strings"
  11. )
  12. const escape = "\033"
  13. // ColorAttribute defines a single SGR Code
  14. type ColorAttribute int
  15. // Base ColorAttributes
  16. const (
  17. Reset ColorAttribute = iota
  18. Bold
  19. Faint
  20. Italic
  21. Underline
  22. BlinkSlow
  23. BlinkRapid
  24. ReverseVideo
  25. Concealed
  26. CrossedOut
  27. )
  28. // Foreground text colors
  29. const (
  30. FgBlack ColorAttribute = iota + 30
  31. FgRed
  32. FgGreen
  33. FgYellow
  34. FgBlue
  35. FgMagenta
  36. FgCyan
  37. FgWhite
  38. )
  39. // Foreground Hi-Intensity text colors
  40. const (
  41. FgHiBlack ColorAttribute = iota + 90
  42. FgHiRed
  43. FgHiGreen
  44. FgHiYellow
  45. FgHiBlue
  46. FgHiMagenta
  47. FgHiCyan
  48. FgHiWhite
  49. )
  50. // Background text colors
  51. const (
  52. BgBlack ColorAttribute = iota + 40
  53. BgRed
  54. BgGreen
  55. BgYellow
  56. BgBlue
  57. BgMagenta
  58. BgCyan
  59. BgWhite
  60. )
  61. // Background Hi-Intensity text colors
  62. const (
  63. BgHiBlack ColorAttribute = iota + 100
  64. BgHiRed
  65. BgHiGreen
  66. BgHiYellow
  67. BgHiBlue
  68. BgHiMagenta
  69. BgHiCyan
  70. BgHiWhite
  71. )
  72. var colorAttributeToString = map[ColorAttribute]string{
  73. Reset: "Reset",
  74. Bold: "Bold",
  75. Faint: "Faint",
  76. Italic: "Italic",
  77. Underline: "Underline",
  78. BlinkSlow: "BlinkSlow",
  79. BlinkRapid: "BlinkRapid",
  80. ReverseVideo: "ReverseVideo",
  81. Concealed: "Concealed",
  82. CrossedOut: "CrossedOut",
  83. FgBlack: "FgBlack",
  84. FgRed: "FgRed",
  85. FgGreen: "FgGreen",
  86. FgYellow: "FgYellow",
  87. FgBlue: "FgBlue",
  88. FgMagenta: "FgMagenta",
  89. FgCyan: "FgCyan",
  90. FgWhite: "FgWhite",
  91. FgHiBlack: "FgHiBlack",
  92. FgHiRed: "FgHiRed",
  93. FgHiGreen: "FgHiGreen",
  94. FgHiYellow: "FgHiYellow",
  95. FgHiBlue: "FgHiBlue",
  96. FgHiMagenta: "FgHiMagenta",
  97. FgHiCyan: "FgHiCyan",
  98. FgHiWhite: "FgHiWhite",
  99. BgBlack: "BgBlack",
  100. BgRed: "BgRed",
  101. BgGreen: "BgGreen",
  102. BgYellow: "BgYellow",
  103. BgBlue: "BgBlue",
  104. BgMagenta: "BgMagenta",
  105. BgCyan: "BgCyan",
  106. BgWhite: "BgWhite",
  107. BgHiBlack: "BgHiBlack",
  108. BgHiRed: "BgHiRed",
  109. BgHiGreen: "BgHiGreen",
  110. BgHiYellow: "BgHiYellow",
  111. BgHiBlue: "BgHiBlue",
  112. BgHiMagenta: "BgHiMagenta",
  113. BgHiCyan: "BgHiCyan",
  114. BgHiWhite: "BgHiWhite",
  115. }
  116. func (c *ColorAttribute) String() string {
  117. return colorAttributeToString[*c]
  118. }
  119. var colorAttributeFromString = map[string]ColorAttribute{}
  120. // ColorAttributeFromString will return a ColorAttribute given a string
  121. func ColorAttributeFromString(from string) ColorAttribute {
  122. lowerFrom := strings.TrimSpace(strings.ToLower(from))
  123. return colorAttributeFromString[lowerFrom]
  124. }
  125. // ColorString converts a list of ColorAttributes to a color string
  126. func ColorString(attrs ...ColorAttribute) string {
  127. return string(ColorBytes(attrs...))
  128. }
  129. // ColorBytes converts a list of ColorAttributes to a byte array
  130. func ColorBytes(attrs ...ColorAttribute) []byte {
  131. bytes := make([]byte, 0, 20)
  132. bytes = append(bytes, escape[0], '[')
  133. if len(attrs) > 0 {
  134. bytes = append(bytes, strconv.Itoa(int(attrs[0]))...)
  135. for _, a := range attrs[1:] {
  136. bytes = append(bytes, ';')
  137. bytes = append(bytes, strconv.Itoa(int(a))...)
  138. }
  139. } else {
  140. bytes = append(bytes, strconv.Itoa(int(Bold))...)
  141. }
  142. bytes = append(bytes, 'm')
  143. return bytes
  144. }
  145. var levelToColor = map[Level]string{
  146. TRACE: ColorString(Bold, FgCyan),
  147. DEBUG: ColorString(Bold, FgBlue),
  148. INFO: ColorString(Bold, FgGreen),
  149. WARN: ColorString(Bold, FgYellow),
  150. ERROR: ColorString(Bold, FgRed),
  151. CRITICAL: ColorString(Bold, BgMagenta),
  152. FATAL: ColorString(Bold, BgRed),
  153. NONE: ColorString(Reset),
  154. }
  155. var resetBytes = ColorBytes(Reset)
  156. var fgCyanBytes = ColorBytes(FgCyan)
  157. var fgGreenBytes = ColorBytes(FgGreen)
  158. var fgBoldBytes = ColorBytes(Bold)
  159. type protectedANSIWriterMode int
  160. const (
  161. escapeAll protectedANSIWriterMode = iota
  162. allowColor
  163. removeColor
  164. )
  165. type protectedANSIWriter struct {
  166. w io.Writer
  167. mode protectedANSIWriterMode
  168. }
  169. // Write will protect against unusual characters
  170. func (c *protectedANSIWriter) Write(bytes []byte) (int, error) {
  171. end := len(bytes)
  172. totalWritten := 0
  173. normalLoop:
  174. for i := 0; i < end; {
  175. lasti := i
  176. if c.mode == escapeAll {
  177. for i < end && (bytes[i] >= ' ' || bytes[i] == '\n' || bytes[i] == '\t') {
  178. i++
  179. }
  180. } else {
  181. // Allow tabs if we're not escaping everything
  182. for i < end && (bytes[i] >= ' ' || bytes[i] == '\t') {
  183. i++
  184. }
  185. }
  186. if i > lasti {
  187. written, err := c.w.Write(bytes[lasti:i])
  188. totalWritten += written
  189. if err != nil {
  190. return totalWritten, err
  191. }
  192. }
  193. if i >= end {
  194. break
  195. }
  196. // If we're not just escaping all we should prefix all newlines with a \t
  197. if c.mode != escapeAll {
  198. if bytes[i] == '\n' {
  199. written, err := c.w.Write([]byte{'\n', '\t'})
  200. if written > 0 {
  201. totalWritten++
  202. }
  203. if err != nil {
  204. return totalWritten, err
  205. }
  206. i++
  207. continue normalLoop
  208. }
  209. if bytes[i] == escape[0] && i+1 < end && bytes[i+1] == '[' {
  210. for j := i + 2; j < end; j++ {
  211. if bytes[j] >= '0' && bytes[j] <= '9' {
  212. continue
  213. }
  214. if bytes[j] == ';' {
  215. continue
  216. }
  217. if bytes[j] == 'm' {
  218. if c.mode == allowColor {
  219. written, err := c.w.Write(bytes[i : j+1])
  220. totalWritten += written
  221. if err != nil {
  222. return totalWritten, err
  223. }
  224. } else {
  225. totalWritten = j
  226. }
  227. i = j + 1
  228. continue normalLoop
  229. }
  230. break
  231. }
  232. }
  233. }
  234. // Process naughty character
  235. if _, err := fmt.Fprintf(c.w, `\%#o03d`, bytes[i]); err != nil {
  236. return totalWritten, err
  237. }
  238. i++
  239. totalWritten++
  240. }
  241. return totalWritten, nil
  242. }
  243. // ColorSprintf returns a colored string from a format and arguments
  244. // arguments will be wrapped in ColoredValues to protect against color spoofing
  245. func ColorSprintf(format string, args ...interface{}) string {
  246. if len(args) > 0 {
  247. v := make([]interface{}, len(args))
  248. for i := 0; i < len(v); i++ {
  249. v[i] = NewColoredValuePointer(&args[i])
  250. }
  251. return fmt.Sprintf(format, v...)
  252. }
  253. return format
  254. }
  255. // ColorFprintf will write to the provided writer similar to ColorSprintf
  256. func ColorFprintf(w io.Writer, format string, args ...interface{}) (int, error) {
  257. if len(args) > 0 {
  258. v := make([]interface{}, len(args))
  259. for i := 0; i < len(v); i++ {
  260. v[i] = NewColoredValuePointer(&args[i])
  261. }
  262. return fmt.Fprintf(w, format, v...)
  263. }
  264. return fmt.Fprint(w, format)
  265. }
  266. // ColorFormatted structs provide their own colored string when formatted with ColorSprintf
  267. type ColorFormatted interface {
  268. // ColorFormat provides the colored representation of the value
  269. ColorFormat(s fmt.State)
  270. }
  271. var colorFormattedType = reflect.TypeOf((*ColorFormatted)(nil)).Elem()
  272. // ColoredValue will Color the provided value
  273. type ColoredValue struct {
  274. colorBytes *[]byte
  275. resetBytes *[]byte
  276. Value *interface{}
  277. }
  278. // NewColoredValue is a helper function to create a ColoredValue from a Value
  279. // If no color is provided it defaults to Bold with standard Reset
  280. // If a ColoredValue is provided it is not changed
  281. func NewColoredValue(value interface{}, color ...ColorAttribute) *ColoredValue {
  282. return NewColoredValuePointer(&value, color...)
  283. }
  284. // NewColoredValuePointer is a helper function to create a ColoredValue from a Value Pointer
  285. // If no color is provided it defaults to Bold with standard Reset
  286. // If a ColoredValue is provided it is not changed
  287. func NewColoredValuePointer(value *interface{}, color ...ColorAttribute) *ColoredValue {
  288. if val, ok := (*value).(*ColoredValue); ok {
  289. return val
  290. }
  291. if len(color) > 0 {
  292. bytes := ColorBytes(color...)
  293. return &ColoredValue{
  294. colorBytes: &bytes,
  295. resetBytes: &resetBytes,
  296. Value: value,
  297. }
  298. }
  299. return &ColoredValue{
  300. colorBytes: &fgBoldBytes,
  301. resetBytes: &resetBytes,
  302. Value: value,
  303. }
  304. }
  305. // NewColoredValueBytes creates a value from the provided value with color bytes
  306. // If a ColoredValue is provided it is not changed
  307. func NewColoredValueBytes(value interface{}, colorBytes *[]byte) *ColoredValue {
  308. if val, ok := value.(*ColoredValue); ok {
  309. return val
  310. }
  311. return &ColoredValue{
  312. colorBytes: colorBytes,
  313. resetBytes: &resetBytes,
  314. Value: &value,
  315. }
  316. }
  317. // NewColoredIDValue is a helper function to create a ColoredValue from a Value
  318. // The Value will be colored with FgCyan
  319. // If a ColoredValue is provided it is not changed
  320. func NewColoredIDValue(value interface{}) *ColoredValue {
  321. return NewColoredValueBytes(&value, &fgCyanBytes)
  322. }
  323. // Format will format the provided value and protect against ANSI color spoofing within the value
  324. // If the wrapped value is ColorFormatted and the format is "%-v" then its ColorString will
  325. // be used. It is presumed that this ColorString is safe.
  326. func (cv *ColoredValue) Format(s fmt.State, c rune) {
  327. if c == 'v' && s.Flag('-') {
  328. if val, ok := (*cv.Value).(ColorFormatted); ok {
  329. val.ColorFormat(s)
  330. return
  331. }
  332. v := reflect.ValueOf(*cv.Value)
  333. t := v.Type()
  334. if reflect.PtrTo(t).Implements(colorFormattedType) {
  335. vp := reflect.New(t)
  336. vp.Elem().Set(v)
  337. val := vp.Interface().(ColorFormatted)
  338. val.ColorFormat(s)
  339. return
  340. }
  341. }
  342. s.Write([]byte(*cv.colorBytes))
  343. fmt.Fprintf(&protectedANSIWriter{w: s}, fmtString(s, c), *(cv.Value))
  344. s.Write([]byte(*cv.resetBytes))
  345. }
  346. // SetColorBytes will allow a user to set the colorBytes of a colored value
  347. func (cv *ColoredValue) SetColorBytes(colorBytes []byte) {
  348. cv.colorBytes = &colorBytes
  349. }
  350. // SetColorBytesPointer will allow a user to set the colorBytes pointer of a colored value
  351. func (cv *ColoredValue) SetColorBytesPointer(colorBytes *[]byte) {
  352. cv.colorBytes = colorBytes
  353. }
  354. // SetResetBytes will allow a user to set the resetBytes pointer of a colored value
  355. func (cv *ColoredValue) SetResetBytes(resetBytes []byte) {
  356. cv.resetBytes = &resetBytes
  357. }
  358. // SetResetBytesPointer will allow a user to set the resetBytes pointer of a colored value
  359. func (cv *ColoredValue) SetResetBytesPointer(resetBytes *[]byte) {
  360. cv.resetBytes = resetBytes
  361. }
  362. func fmtString(s fmt.State, c rune) string {
  363. var width, precision string
  364. base := make([]byte, 0, 8)
  365. base = append(base, '%')
  366. for _, c := range []byte(" +-#0") {
  367. if s.Flag(int(c)) {
  368. base = append(base, c)
  369. }
  370. }
  371. if w, ok := s.Width(); ok {
  372. width = strconv.Itoa(w)
  373. }
  374. if p, ok := s.Precision(); ok {
  375. precision = "." + strconv.Itoa(p)
  376. }
  377. return fmt.Sprintf("%s%s%s%c", base, width, precision, c)
  378. }
  379. func init() {
  380. for attr, from := range colorAttributeToString {
  381. colorAttributeFromString[strings.ToLower(from)] = attr
  382. }
  383. }