1// +build windows
2// +build !appengine
3
4package colorable
5
6import (
7	"bytes"
8	"io"
9	"math"
10	"os"
11	"strconv"
12	"strings"
13	"sync"
14	"syscall"
15	"unsafe"
16
17	"github.com/mattn/go-isatty"
18)
19
20const (
21	foregroundBlue      = 0x1
22	foregroundGreen     = 0x2
23	foregroundRed       = 0x4
24	foregroundIntensity = 0x8
25	foregroundMask      = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
26	backgroundBlue      = 0x10
27	backgroundGreen     = 0x20
28	backgroundRed       = 0x40
29	backgroundIntensity = 0x80
30	backgroundMask      = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
31	commonLvbUnderscore = 0x8000
32
33	cENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
34)
35
36const (
37	genericRead  = 0x80000000
38	genericWrite = 0x40000000
39)
40
41const (
42	consoleTextmodeBuffer = 0x1
43)
44
45type wchar uint16
46type short int16
47type dword uint32
48type word uint16
49
50type coord struct {
51	x short
52	y short
53}
54
55type smallRect struct {
56	left   short
57	top    short
58	right  short
59	bottom short
60}
61
62type consoleScreenBufferInfo struct {
63	size              coord
64	cursorPosition    coord
65	attributes        word
66	window            smallRect
67	maximumWindowSize coord
68}
69
70type consoleCursorInfo struct {
71	size    dword
72	visible int32
73}
74
75var (
76	kernel32                       = syscall.NewLazyDLL("kernel32.dll")
77	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
78	procSetConsoleTextAttribute    = kernel32.NewProc("SetConsoleTextAttribute")
79	procSetConsoleCursorPosition   = kernel32.NewProc("SetConsoleCursorPosition")
80	procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
81	procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
82	procGetConsoleCursorInfo       = kernel32.NewProc("GetConsoleCursorInfo")
83	procSetConsoleCursorInfo       = kernel32.NewProc("SetConsoleCursorInfo")
84	procSetConsoleTitle            = kernel32.NewProc("SetConsoleTitleW")
85	procGetConsoleMode             = kernel32.NewProc("GetConsoleMode")
86	procSetConsoleMode             = kernel32.NewProc("SetConsoleMode")
87	procCreateConsoleScreenBuffer  = kernel32.NewProc("CreateConsoleScreenBuffer")
88)
89
90// Writer provides colorable Writer to the console
91type Writer struct {
92	out       io.Writer
93	handle    syscall.Handle
94	althandle syscall.Handle
95	oldattr   word
96	oldpos    coord
97	rest      bytes.Buffer
98	mutex     sync.Mutex
99}
100
101// NewColorable returns new instance of Writer which handles escape sequence from File.
102func NewColorable(file *os.File) io.Writer {
103	if file == nil {
104		panic("nil passed instead of *os.File to NewColorable()")
105	}
106
107	if isatty.IsTerminal(file.Fd()) {
108		var mode uint32
109		if r, _, _ := procGetConsoleMode.Call(file.Fd(), uintptr(unsafe.Pointer(&mode))); r != 0 && mode&cENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 {
110			return file
111		}
112		var csbi consoleScreenBufferInfo
113		handle := syscall.Handle(file.Fd())
114		procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
115		return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}}
116	}
117	return file
118}
119
120// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
121func NewColorableStdout() io.Writer {
122	return NewColorable(os.Stdout)
123}
124
125// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
126func NewColorableStderr() io.Writer {
127	return NewColorable(os.Stderr)
128}
129
130var color256 = map[int]int{
131	0:   0x000000,
132	1:   0x800000,
133	2:   0x008000,
134	3:   0x808000,
135	4:   0x000080,
136	5:   0x800080,
137	6:   0x008080,
138	7:   0xc0c0c0,
139	8:   0x808080,
140	9:   0xff0000,
141	10:  0x00ff00,
142	11:  0xffff00,
143	12:  0x0000ff,
144	13:  0xff00ff,
145	14:  0x00ffff,
146	15:  0xffffff,
147	16:  0x000000,
148	17:  0x00005f,
149	18:  0x000087,
150	19:  0x0000af,
151	20:  0x0000d7,
152	21:  0x0000ff,
153	22:  0x005f00,
154	23:  0x005f5f,
155	24:  0x005f87,
156	25:  0x005faf,
157	26:  0x005fd7,
158	27:  0x005fff,
159	28:  0x008700,
160	29:  0x00875f,
161	30:  0x008787,
162	31:  0x0087af,
163	32:  0x0087d7,
164	33:  0x0087ff,
165	34:  0x00af00,
166	35:  0x00af5f,
167	36:  0x00af87,
168	37:  0x00afaf,
169	38:  0x00afd7,
170	39:  0x00afff,
171	40:  0x00d700,
172	41:  0x00d75f,
173	42:  0x00d787,
174	43:  0x00d7af,
175	44:  0x00d7d7,
176	45:  0x00d7ff,
177	46:  0x00ff00,
178	47:  0x00ff5f,
179	48:  0x00ff87,
180	49:  0x00ffaf,
181	50:  0x00ffd7,
182	51:  0x00ffff,
183	52:  0x5f0000,
184	53:  0x5f005f,
185	54:  0x5f0087,
186	55:  0x5f00af,
187	56:  0x5f00d7,
188	57:  0x5f00ff,
189	58:  0x5f5f00,
190	59:  0x5f5f5f,
191	60:  0x5f5f87,
192	61:  0x5f5faf,
193	62:  0x5f5fd7,
194	63:  0x5f5fff,
195	64:  0x5f8700,
196	65:  0x5f875f,
197	66:  0x5f8787,
198	67:  0x5f87af,
199	68:  0x5f87d7,
200	69:  0x5f87ff,
201	70:  0x5faf00,
202	71:  0x5faf5f,
203	72:  0x5faf87,
204	73:  0x5fafaf,
205	74:  0x5fafd7,
206	75:  0x5fafff,
207	76:  0x5fd700,
208	77:  0x5fd75f,
209	78:  0x5fd787,
210	79:  0x5fd7af,
211	80:  0x5fd7d7,
212	81:  0x5fd7ff,
213	82:  0x5fff00,
214	83:  0x5fff5f,
215	84:  0x5fff87,
216	85:  0x5fffaf,
217	86:  0x5fffd7,
218	87:  0x5fffff,
219	88:  0x870000,
220	89:  0x87005f,
221	90:  0x870087,
222	91:  0x8700af,
223	92:  0x8700d7,
224	93:  0x8700ff,
225	94:  0x875f00,
226	95:  0x875f5f,
227	96:  0x875f87,
228	97:  0x875faf,
229	98:  0x875fd7,
230	99:  0x875fff,
231	100: 0x878700,
232	101: 0x87875f,
233	102: 0x878787,
234	103: 0x8787af,
235	104: 0x8787d7,
236	105: 0x8787ff,
237	106: 0x87af00,
238	107: 0x87af5f,
239	108: 0x87af87,
240	109: 0x87afaf,
241	110: 0x87afd7,
242	111: 0x87afff,
243	112: 0x87d700,
244	113: 0x87d75f,
245	114: 0x87d787,
246	115: 0x87d7af,
247	116: 0x87d7d7,
248	117: 0x87d7ff,
249	118: 0x87ff00,
250	119: 0x87ff5f,
251	120: 0x87ff87,
252	121: 0x87ffaf,
253	122: 0x87ffd7,
254	123: 0x87ffff,
255	124: 0xaf0000,
256	125: 0xaf005f,
257	126: 0xaf0087,
258	127: 0xaf00af,
259	128: 0xaf00d7,
260	129: 0xaf00ff,
261	130: 0xaf5f00,
262	131: 0xaf5f5f,
263	132: 0xaf5f87,
264	133: 0xaf5faf,
265	134: 0xaf5fd7,
266	135: 0xaf5fff,
267	136: 0xaf8700,
268	137: 0xaf875f,
269	138: 0xaf8787,
270	139: 0xaf87af,
271	140: 0xaf87d7,
272	141: 0xaf87ff,
273	142: 0xafaf00,
274	143: 0xafaf5f,
275	144: 0xafaf87,
276	145: 0xafafaf,
277	146: 0xafafd7,
278	147: 0xafafff,
279	148: 0xafd700,
280	149: 0xafd75f,
281	150: 0xafd787,
282	151: 0xafd7af,
283	152: 0xafd7d7,
284	153: 0xafd7ff,
285	154: 0xafff00,
286	155: 0xafff5f,
287	156: 0xafff87,
288	157: 0xafffaf,
289	158: 0xafffd7,
290	159: 0xafffff,
291	160: 0xd70000,
292	161: 0xd7005f,
293	162: 0xd70087,
294	163: 0xd700af,
295	164: 0xd700d7,
296	165: 0xd700ff,
297	166: 0xd75f00,
298	167: 0xd75f5f,
299	168: 0xd75f87,
300	169: 0xd75faf,
301	170: 0xd75fd7,
302	171: 0xd75fff,
303	172: 0xd78700,
304	173: 0xd7875f,
305	174: 0xd78787,
306	175: 0xd787af,
307	176: 0xd787d7,
308	177: 0xd787ff,
309	178: 0xd7af00,
310	179: 0xd7af5f,
311	180: 0xd7af87,
312	181: 0xd7afaf,
313	182: 0xd7afd7,
314	183: 0xd7afff,
315	184: 0xd7d700,
316	185: 0xd7d75f,
317	186: 0xd7d787,
318	187: 0xd7d7af,
319	188: 0xd7d7d7,
320	189: 0xd7d7ff,
321	190: 0xd7ff00,
322	191: 0xd7ff5f,
323	192: 0xd7ff87,
324	193: 0xd7ffaf,
325	194: 0xd7ffd7,
326	195: 0xd7ffff,
327	196: 0xff0000,
328	197: 0xff005f,
329	198: 0xff0087,
330	199: 0xff00af,
331	200: 0xff00d7,
332	201: 0xff00ff,
333	202: 0xff5f00,
334	203: 0xff5f5f,
335	204: 0xff5f87,
336	205: 0xff5faf,
337	206: 0xff5fd7,
338	207: 0xff5fff,
339	208: 0xff8700,
340	209: 0xff875f,
341	210: 0xff8787,
342	211: 0xff87af,
343	212: 0xff87d7,
344	213: 0xff87ff,
345	214: 0xffaf00,
346	215: 0xffaf5f,
347	216: 0xffaf87,
348	217: 0xffafaf,
349	218: 0xffafd7,
350	219: 0xffafff,
351	220: 0xffd700,
352	221: 0xffd75f,
353	222: 0xffd787,
354	223: 0xffd7af,
355	224: 0xffd7d7,
356	225: 0xffd7ff,
357	226: 0xffff00,
358	227: 0xffff5f,
359	228: 0xffff87,
360	229: 0xffffaf,
361	230: 0xffffd7,
362	231: 0xffffff,
363	232: 0x080808,
364	233: 0x121212,
365	234: 0x1c1c1c,
366	235: 0x262626,
367	236: 0x303030,
368	237: 0x3a3a3a,
369	238: 0x444444,
370	239: 0x4e4e4e,
371	240: 0x585858,
372	241: 0x626262,
373	242: 0x6c6c6c,
374	243: 0x767676,
375	244: 0x808080,
376	245: 0x8a8a8a,
377	246: 0x949494,
378	247: 0x9e9e9e,
379	248: 0xa8a8a8,
380	249: 0xb2b2b2,
381	250: 0xbcbcbc,
382	251: 0xc6c6c6,
383	252: 0xd0d0d0,
384	253: 0xdadada,
385	254: 0xe4e4e4,
386	255: 0xeeeeee,
387}
388
389// `\033]0;TITLESTR\007`
390func doTitleSequence(er *bytes.Reader) error {
391	var c byte
392	var err error
393
394	c, err = er.ReadByte()
395	if err != nil {
396		return err
397	}
398	if c != '0' && c != '2' {
399		return nil
400	}
401	c, err = er.ReadByte()
402	if err != nil {
403		return err
404	}
405	if c != ';' {
406		return nil
407	}
408	title := make([]byte, 0, 80)
409	for {
410		c, err = er.ReadByte()
411		if err != nil {
412			return err
413		}
414		if c == 0x07 || c == '\n' {
415			break
416		}
417		title = append(title, c)
418	}
419	if len(title) > 0 {
420		title8, err := syscall.UTF16PtrFromString(string(title))
421		if err == nil {
422			procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8)))
423		}
424	}
425	return nil
426}
427
428// returns Atoi(s) unless s == "" in which case it returns def
429func atoiWithDefault(s string, def int) (int, error) {
430	if s == "" {
431		return def, nil
432	}
433	return strconv.Atoi(s)
434}
435
436// Write writes data on console
437func (w *Writer) Write(data []byte) (n int, err error) {
438	w.mutex.Lock()
439	defer w.mutex.Unlock()
440	var csbi consoleScreenBufferInfo
441	procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
442
443	handle := w.handle
444
445	var er *bytes.Reader
446	if w.rest.Len() > 0 {
447		var rest bytes.Buffer
448		w.rest.WriteTo(&rest)
449		w.rest.Reset()
450		rest.Write(data)
451		er = bytes.NewReader(rest.Bytes())
452	} else {
453		er = bytes.NewReader(data)
454	}
455	var bw [1]byte
456loop:
457	for {
458		c1, err := er.ReadByte()
459		if err != nil {
460			break loop
461		}
462		if c1 != 0x1b {
463			bw[0] = c1
464			w.out.Write(bw[:])
465			continue
466		}
467		c2, err := er.ReadByte()
468		if err != nil {
469			break loop
470		}
471
472		switch c2 {
473		case '>':
474			continue
475		case ']':
476			w.rest.WriteByte(c1)
477			w.rest.WriteByte(c2)
478			er.WriteTo(&w.rest)
479			if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 {
480				break loop
481			}
482			er = bytes.NewReader(w.rest.Bytes()[2:])
483			err := doTitleSequence(er)
484			if err != nil {
485				break loop
486			}
487			w.rest.Reset()
488			continue
489		// https://github.com/mattn/go-colorable/issues/27
490		case '7':
491			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
492			w.oldpos = csbi.cursorPosition
493			continue
494		case '8':
495			procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
496			continue
497		case 0x5b:
498			// execute part after switch
499		default:
500			continue
501		}
502
503		w.rest.WriteByte(c1)
504		w.rest.WriteByte(c2)
505		er.WriteTo(&w.rest)
506
507		var buf bytes.Buffer
508		var m byte
509		for i, c := range w.rest.Bytes()[2:] {
510			if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
511				m = c
512				er = bytes.NewReader(w.rest.Bytes()[2+i+1:])
513				w.rest.Reset()
514				break
515			}
516			buf.Write([]byte(string(c)))
517		}
518		if m == 0 {
519			break loop
520		}
521
522		switch m {
523		case 'A':
524			n, err = atoiWithDefault(buf.String(), 1)
525			if err != nil {
526				continue
527			}
528			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
529			csbi.cursorPosition.y -= short(n)
530			procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
531		case 'B':
532			n, err = atoiWithDefault(buf.String(), 1)
533			if err != nil {
534				continue
535			}
536			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
537			csbi.cursorPosition.y += short(n)
538			procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
539		case 'C':
540			n, err = atoiWithDefault(buf.String(), 1)
541			if err != nil {
542				continue
543			}
544			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
545			csbi.cursorPosition.x += short(n)
546			procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
547		case 'D':
548			n, err = atoiWithDefault(buf.String(), 1)
549			if err != nil {
550				continue
551			}
552			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
553			csbi.cursorPosition.x -= short(n)
554			if csbi.cursorPosition.x < 0 {
555				csbi.cursorPosition.x = 0
556			}
557			procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
558		case 'E':
559			n, err = strconv.Atoi(buf.String())
560			if err != nil {
561				continue
562			}
563			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
564			csbi.cursorPosition.x = 0
565			csbi.cursorPosition.y += short(n)
566			procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
567		case 'F':
568			n, err = strconv.Atoi(buf.String())
569			if err != nil {
570				continue
571			}
572			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
573			csbi.cursorPosition.x = 0
574			csbi.cursorPosition.y -= short(n)
575			procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
576		case 'G':
577			n, err = strconv.Atoi(buf.String())
578			if err != nil {
579				continue
580			}
581			if n < 1 {
582				n = 1
583			}
584			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
585			csbi.cursorPosition.x = short(n - 1)
586			procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
587		case 'H', 'f':
588			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
589			if buf.Len() > 0 {
590				token := strings.Split(buf.String(), ";")
591				switch len(token) {
592				case 1:
593					n1, err := strconv.Atoi(token[0])
594					if err != nil {
595						continue
596					}
597					csbi.cursorPosition.y = short(n1 - 1)
598				case 2:
599					n1, err := strconv.Atoi(token[0])
600					if err != nil {
601						continue
602					}
603					n2, err := strconv.Atoi(token[1])
604					if err != nil {
605						continue
606					}
607					csbi.cursorPosition.x = short(n2 - 1)
608					csbi.cursorPosition.y = short(n1 - 1)
609				}
610			} else {
611				csbi.cursorPosition.y = 0
612			}
613			procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
614		case 'J':
615			n := 0
616			if buf.Len() > 0 {
617				n, err = strconv.Atoi(buf.String())
618				if err != nil {
619					continue
620				}
621			}
622			var count, written dword
623			var cursor coord
624			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
625			switch n {
626			case 0:
627				cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
628				count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x)
629			case 1:
630				cursor = coord{x: csbi.window.left, y: csbi.window.top}
631				count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x)
632			case 2:
633				cursor = coord{x: csbi.window.left, y: csbi.window.top}
634				count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x)
635			}
636			procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
637			procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
638		case 'K':
639			n := 0
640			if buf.Len() > 0 {
641				n, err = strconv.Atoi(buf.String())
642				if err != nil {
643					continue
644				}
645			}
646			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
647			var cursor coord
648			var count, written dword
649			switch n {
650			case 0:
651				cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
652				count = dword(csbi.size.x - csbi.cursorPosition.x)
653			case 1:
654				cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y}
655				count = dword(csbi.size.x - csbi.cursorPosition.x)
656			case 2:
657				cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y}
658				count = dword(csbi.size.x)
659			}
660			procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
661			procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
662		case 'X':
663			n := 0
664			if buf.Len() > 0 {
665				n, err = strconv.Atoi(buf.String())
666				if err != nil {
667					continue
668				}
669			}
670			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
671			var cursor coord
672			var written dword
673			cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
674			procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
675			procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
676		case 'm':
677			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
678			attr := csbi.attributes
679			cs := buf.String()
680			if cs == "" {
681				procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr))
682				continue
683			}
684			token := strings.Split(cs, ";")
685			for i := 0; i < len(token); i++ {
686				ns := token[i]
687				if n, err = strconv.Atoi(ns); err == nil {
688					switch {
689					case n == 0 || n == 100:
690						attr = w.oldattr
691					case n == 4:
692						attr |= commonLvbUnderscore
693					case (1 <= n && n <= 3) || n == 5:
694						attr |= foregroundIntensity
695					case n == 7 || n == 27:
696						attr =
697							(attr &^ (foregroundMask | backgroundMask)) |
698								((attr & foregroundMask) << 4) |
699								((attr & backgroundMask) >> 4)
700					case n == 22:
701						attr &^= foregroundIntensity
702					case n == 24:
703						attr &^= commonLvbUnderscore
704					case 30 <= n && n <= 37:
705						attr &= backgroundMask
706						if (n-30)&1 != 0 {
707							attr |= foregroundRed
708						}
709						if (n-30)&2 != 0 {
710							attr |= foregroundGreen
711						}
712						if (n-30)&4 != 0 {
713							attr |= foregroundBlue
714						}
715					case n == 38: // set foreground color.
716						if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") {
717							if n256, err := strconv.Atoi(token[i+2]); err == nil {
718								if n256foreAttr == nil {
719									n256setup()
720								}
721								attr &= backgroundMask
722								attr |= n256foreAttr[n256%len(n256foreAttr)]
723								i += 2
724							}
725						} else if len(token) == 5 && token[i+1] == "2" {
726							var r, g, b int
727							r, _ = strconv.Atoi(token[i+2])
728							g, _ = strconv.Atoi(token[i+3])
729							b, _ = strconv.Atoi(token[i+4])
730							i += 4
731							if r > 127 {
732								attr |= foregroundRed
733							}
734							if g > 127 {
735								attr |= foregroundGreen
736							}
737							if b > 127 {
738								attr |= foregroundBlue
739							}
740						} else {
741							attr = attr & (w.oldattr & backgroundMask)
742						}
743					case n == 39: // reset foreground color.
744						attr &= backgroundMask
745						attr |= w.oldattr & foregroundMask
746					case 40 <= n && n <= 47:
747						attr &= foregroundMask
748						if (n-40)&1 != 0 {
749							attr |= backgroundRed
750						}
751						if (n-40)&2 != 0 {
752							attr |= backgroundGreen
753						}
754						if (n-40)&4 != 0 {
755							attr |= backgroundBlue
756						}
757					case n == 48: // set background color.
758						if i < len(token)-2 && token[i+1] == "5" {
759							if n256, err := strconv.Atoi(token[i+2]); err == nil {
760								if n256backAttr == nil {
761									n256setup()
762								}
763								attr &= foregroundMask
764								attr |= n256backAttr[n256%len(n256backAttr)]
765								i += 2
766							}
767						} else if len(token) == 5 && token[i+1] == "2" {
768							var r, g, b int
769							r, _ = strconv.Atoi(token[i+2])
770							g, _ = strconv.Atoi(token[i+3])
771							b, _ = strconv.Atoi(token[i+4])
772							i += 4
773							if r > 127 {
774								attr |= backgroundRed
775							}
776							if g > 127 {
777								attr |= backgroundGreen
778							}
779							if b > 127 {
780								attr |= backgroundBlue
781							}
782						} else {
783							attr = attr & (w.oldattr & foregroundMask)
784						}
785					case n == 49: // reset foreground color.
786						attr &= foregroundMask
787						attr |= w.oldattr & backgroundMask
788					case 90 <= n && n <= 97:
789						attr = (attr & backgroundMask)
790						attr |= foregroundIntensity
791						if (n-90)&1 != 0 {
792							attr |= foregroundRed
793						}
794						if (n-90)&2 != 0 {
795							attr |= foregroundGreen
796						}
797						if (n-90)&4 != 0 {
798							attr |= foregroundBlue
799						}
800					case 100 <= n && n <= 107:
801						attr = (attr & foregroundMask)
802						attr |= backgroundIntensity
803						if (n-100)&1 != 0 {
804							attr |= backgroundRed
805						}
806						if (n-100)&2 != 0 {
807							attr |= backgroundGreen
808						}
809						if (n-100)&4 != 0 {
810							attr |= backgroundBlue
811						}
812					}
813					procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr))
814				}
815			}
816		case 'h':
817			var ci consoleCursorInfo
818			cs := buf.String()
819			if cs == "5>" {
820				procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
821				ci.visible = 0
822				procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
823			} else if cs == "?25" {
824				procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
825				ci.visible = 1
826				procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
827			} else if cs == "?1049" {
828				if w.althandle == 0 {
829					h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0)
830					w.althandle = syscall.Handle(h)
831					if w.althandle != 0 {
832						handle = w.althandle
833					}
834				}
835			}
836		case 'l':
837			var ci consoleCursorInfo
838			cs := buf.String()
839			if cs == "5>" {
840				procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
841				ci.visible = 1
842				procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
843			} else if cs == "?25" {
844				procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
845				ci.visible = 0
846				procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
847			} else if cs == "?1049" {
848				if w.althandle != 0 {
849					syscall.CloseHandle(w.althandle)
850					w.althandle = 0
851					handle = w.handle
852				}
853			}
854		case 's':
855			procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
856			w.oldpos = csbi.cursorPosition
857		case 'u':
858			procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
859		}
860	}
861
862	return len(data), nil
863}
864
865type consoleColor struct {
866	rgb       int
867	red       bool
868	green     bool
869	blue      bool
870	intensity bool
871}
872
873func (c consoleColor) foregroundAttr() (attr word) {
874	if c.red {
875		attr |= foregroundRed
876	}
877	if c.green {
878		attr |= foregroundGreen
879	}
880	if c.blue {
881		attr |= foregroundBlue
882	}
883	if c.intensity {
884		attr |= foregroundIntensity
885	}
886	return
887}
888
889func (c consoleColor) backgroundAttr() (attr word) {
890	if c.red {
891		attr |= backgroundRed
892	}
893	if c.green {
894		attr |= backgroundGreen
895	}
896	if c.blue {
897		attr |= backgroundBlue
898	}
899	if c.intensity {
900		attr |= backgroundIntensity
901	}
902	return
903}
904
905var color16 = []consoleColor{
906	{0x000000, false, false, false, false},
907	{0x000080, false, false, true, false},
908	{0x008000, false, true, false, false},
909	{0x008080, false, true, true, false},
910	{0x800000, true, false, false, false},
911	{0x800080, true, false, true, false},
912	{0x808000, true, true, false, false},
913	{0xc0c0c0, true, true, true, false},
914	{0x808080, false, false, false, true},
915	{0x0000ff, false, false, true, true},
916	{0x00ff00, false, true, false, true},
917	{0x00ffff, false, true, true, true},
918	{0xff0000, true, false, false, true},
919	{0xff00ff, true, false, true, true},
920	{0xffff00, true, true, false, true},
921	{0xffffff, true, true, true, true},
922}
923
924type hsv struct {
925	h, s, v float32
926}
927
928func (a hsv) dist(b hsv) float32 {
929	dh := a.h - b.h
930	switch {
931	case dh > 0.5:
932		dh = 1 - dh
933	case dh < -0.5:
934		dh = -1 - dh
935	}
936	ds := a.s - b.s
937	dv := a.v - b.v
938	return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv)))
939}
940
941func toHSV(rgb int) hsv {
942	r, g, b := float32((rgb&0xFF0000)>>16)/256.0,
943		float32((rgb&0x00FF00)>>8)/256.0,
944		float32(rgb&0x0000FF)/256.0
945	min, max := minmax3f(r, g, b)
946	h := max - min
947	if h > 0 {
948		if max == r {
949			h = (g - b) / h
950			if h < 0 {
951				h += 6
952			}
953		} else if max == g {
954			h = 2 + (b-r)/h
955		} else {
956			h = 4 + (r-g)/h
957		}
958	}
959	h /= 6.0
960	s := max - min
961	if max != 0 {
962		s /= max
963	}
964	v := max
965	return hsv{h: h, s: s, v: v}
966}
967
968type hsvTable []hsv
969
970func toHSVTable(rgbTable []consoleColor) hsvTable {
971	t := make(hsvTable, len(rgbTable))
972	for i, c := range rgbTable {
973		t[i] = toHSV(c.rgb)
974	}
975	return t
976}
977
978func (t hsvTable) find(rgb int) consoleColor {
979	hsv := toHSV(rgb)
980	n := 7
981	l := float32(5.0)
982	for i, p := range t {
983		d := hsv.dist(p)
984		if d < l {
985			l, n = d, i
986		}
987	}
988	return color16[n]
989}
990
991func minmax3f(a, b, c float32) (min, max float32) {
992	if a < b {
993		if b < c {
994			return a, c
995		} else if a < c {
996			return a, b
997		} else {
998			return c, b
999		}
1000	} else {
1001		if a < c {
1002			return b, c
1003		} else if b < c {
1004			return b, a
1005		} else {
1006			return c, a
1007		}
1008	}
1009}
1010
1011var n256foreAttr []word
1012var n256backAttr []word
1013
1014func n256setup() {
1015	n256foreAttr = make([]word, 256)
1016	n256backAttr = make([]word, 256)
1017	t := toHSVTable(color16)
1018	for i, rgb := range color256 {
1019		c := t.find(rgb)
1020		n256foreAttr[i] = c.foregroundAttr()
1021		n256backAttr[i] = c.backgroundAttr()
1022	}
1023}
1024
1025// EnableColorsStdout enable colors if possible.
1026func EnableColorsStdout(enabled *bool) func() {
1027	var mode uint32
1028	h := os.Stdout.Fd()
1029	if r, _, _ := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&mode))); r != 0 {
1030		if r, _, _ = procSetConsoleMode.Call(h, uintptr(mode|cENABLE_VIRTUAL_TERMINAL_PROCESSING)); r != 0 {
1031			if enabled != nil {
1032				*enabled = true
1033			}
1034			return func() {
1035				procSetConsoleMode.Call(h, uintptr(mode))
1036			}
1037		}
1038	}
1039	if enabled != nil {
1040		*enabled = true
1041	}
1042	return func() {}
1043}
1044