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