barometer: update DMA's vendoring packages
[barometer.git] / src / dma / vendor / github.com / mattn / go-colorable / colorable_windows.go
1 // +build windows
2 // +build !appengine
3
4 package colorable
5
6 import (
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
19 const (
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
32 const (
33         genericRead  = 0x80000000
34         genericWrite = 0x40000000
35 )
36
37 const (
38         consoleTextmodeBuffer = 0x1
39 )
40
41 type wchar uint16
42 type short int16
43 type dword uint32
44 type word uint16
45
46 type coord struct {
47         x short
48         y short
49 }
50
51 type smallRect struct {
52         left   short
53         top    short
54         right  short
55         bottom short
56 }
57
58 type consoleScreenBufferInfo struct {
59         size              coord
60         cursorPosition    coord
61         attributes        word
62         window            smallRect
63         maximumWindowSize coord
64 }
65
66 type consoleCursorInfo struct {
67         size    dword
68         visible int32
69 }
70
71 var (
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
85 type 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.
95 func 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.
110 func NewColorableStdout() io.Writer {
111         return NewColorable(os.Stdout)
112 }
113
114 // NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
115 func NewColorableStderr() io.Writer {
116         return NewColorable(os.Stderr)
117 }
118
119 var 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`
379 func 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
418 func (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
435 loop:
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
822 type consoleColor struct {
823         rgb       int
824         red       bool
825         green     bool
826         blue      bool
827         intensity bool
828 }
829
830 func (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
846 func (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
862 var 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
881 type hsv struct {
882         h, s, v float32
883 }
884
885 func (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
898 func 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
925 type hsvTable []hsv
926
927 func 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
935 func (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
948 func 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
968 var n256foreAttr []word
969 var n256backAttr []word
970
971 func 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 }