a geicko-2 based round robin ranking system designed to test c++ battleship submissions battleship.dunkirk.sh
at main 13 kB view raw
1// Package terminfo implements reading terminfo files in pure go. 2package terminfo 3 4//go:generate go run gen.go 5 6import ( 7 "io" 8 "io/ioutil" 9 "path" 10 "strconv" 11 "strings" 12) 13 14// Error is a terminfo error. 15type Error string 16 17// Error satisfies the error interface. 18func (err Error) Error() string { 19 return string(err) 20} 21 22const ( 23 // ErrInvalidFileSize is the invalid file size error. 24 ErrInvalidFileSize Error = "invalid file size" 25 // ErrUnexpectedFileEnd is the unexpected file end error. 26 ErrUnexpectedFileEnd Error = "unexpected file end" 27 // ErrInvalidStringTable is the invalid string table error. 28 ErrInvalidStringTable Error = "invalid string table" 29 // ErrInvalidMagic is the invalid magic error. 30 ErrInvalidMagic Error = "invalid magic" 31 // ErrInvalidHeader is the invalid header error. 32 ErrInvalidHeader Error = "invalid header" 33 // ErrInvalidNames is the invalid names error. 34 ErrInvalidNames Error = "invalid names" 35 // ErrInvalidExtendedHeader is the invalid extended header error. 36 ErrInvalidExtendedHeader Error = "invalid extended header" 37 // ErrEmptyTermName is the empty term name error. 38 ErrEmptyTermName Error = "empty term name" 39 // ErrDatabaseDirectoryNotFound is the database directory not found error. 40 ErrDatabaseDirectoryNotFound Error = "database directory not found" 41 // ErrFileNotFound is the file not found error. 42 ErrFileNotFound Error = "file not found" 43 // ErrInvalidTermProgramVersion is the invalid TERM_PROGRAM_VERSION error. 44 ErrInvalidTermProgramVersion Error = "invalid TERM_PROGRAM_VERSION" 45) 46 47// Terminfo describes a terminal's capabilities. 48type Terminfo struct { 49 // File is the original source file. 50 File string 51 // Names are the provided cap names. 52 Names []string 53 // Bools are the bool capabilities. 54 Bools map[int]bool 55 // BoolsM are the missing bool capabilities. 56 BoolsM map[int]bool 57 // Nums are the num capabilities. 58 Nums map[int]int 59 // NumsM are the missing num capabilities. 60 NumsM map[int]bool 61 // Strings are the string capabilities. 62 Strings map[int][]byte 63 // StringsM are the missing string capabilities. 64 StringsM map[int]bool 65 // ExtBools are the extended bool capabilities. 66 ExtBools map[int]bool 67 // ExtBoolsNames is the map of extended bool capabilities to their index. 68 ExtBoolNames map[int][]byte 69 // ExtNums are the extended num capabilities. 70 ExtNums map[int]int 71 // ExtNumsNames is the map of extended num capabilities to their index. 72 ExtNumNames map[int][]byte 73 // ExtStrings are the extended string capabilities. 74 ExtStrings map[int][]byte 75 // ExtStringsNames is the map of extended string capabilities to their index. 76 ExtStringNames map[int][]byte 77} 78 79// Decode decodes the terminfo data contained in buf. 80func Decode(buf []byte) (*Terminfo, error) { 81 var err error 82 // check max file length 83 if len(buf) >= maxFileLength { 84 return nil, ErrInvalidFileSize 85 } 86 d := &decoder{ 87 buf: buf, 88 n: len(buf), 89 } 90 // read header 91 h, err := d.readInts(6, 16) 92 if err != nil { 93 return nil, err 94 } 95 var numWidth int 96 // check magic 97 switch { 98 case h[fieldMagic] == magic: 99 numWidth = 16 100 case h[fieldMagic] == magicExtended: 101 numWidth = 32 102 default: 103 return nil, ErrInvalidMagic 104 } 105 // check header 106 if hasInvalidCaps(h) { 107 return nil, ErrInvalidHeader 108 } 109 // check remaining length 110 if d.n-d.pos < capLength(h) { 111 return nil, ErrUnexpectedFileEnd 112 } 113 // read names 114 names, err := d.readBytes(h[fieldNameSize]) 115 if err != nil { 116 return nil, err 117 } 118 // check name is terminated properly 119 i := findNull(names, 0) 120 if i == -1 { 121 return nil, ErrInvalidNames 122 } 123 names = names[:i] 124 // read bool caps 125 bools, boolsM, err := d.readBools(h[fieldBoolCount]) 126 if err != nil { 127 return nil, err 128 } 129 // read num caps 130 nums, numsM, err := d.readNums(h[fieldNumCount], numWidth) 131 if err != nil { 132 return nil, err 133 } 134 // read string caps 135 strs, strsM, err := d.readStrings(h[fieldStringCount], h[fieldTableSize]) 136 if err != nil { 137 return nil, err 138 } 139 ti := &Terminfo{ 140 Names: strings.Split(string(names), "|"), 141 Bools: bools, 142 BoolsM: boolsM, 143 Nums: nums, 144 NumsM: numsM, 145 Strings: strs, 146 StringsM: strsM, 147 } 148 // at the end of file, so no extended caps 149 if d.pos >= d.n { 150 return ti, nil 151 } 152 // decode extended header 153 eh, err := d.readInts(5, 16) 154 if err != nil { 155 return nil, err 156 } 157 // check extended offset field 158 if hasInvalidExtOffset(eh) { 159 return nil, ErrInvalidExtendedHeader 160 } 161 // check extended cap lengths 162 if d.n-d.pos != extCapLength(eh, numWidth) { 163 return nil, ErrInvalidExtendedHeader 164 } 165 // read extended bool caps 166 ti.ExtBools, _, err = d.readBools(eh[fieldExtBoolCount]) 167 if err != nil { 168 return nil, err 169 } 170 // read extended num caps 171 ti.ExtNums, _, err = d.readNums(eh[fieldExtNumCount], numWidth) 172 if err != nil { 173 return nil, err 174 } 175 // read extended string data table indexes 176 extIndexes, err := d.readInts(eh[fieldExtOffsetCount], 16) 177 if err != nil { 178 return nil, err 179 } 180 // read string data table 181 extData, err := d.readBytes(eh[fieldExtTableSize]) 182 if err != nil { 183 return nil, err 184 } 185 // precautionary check that exactly at end of file 186 if d.pos != d.n { 187 return nil, ErrUnexpectedFileEnd 188 } 189 var last int 190 // read extended string caps 191 ti.ExtStrings, last, err = readStrings(extIndexes, extData, eh[fieldExtStringCount]) 192 if err != nil { 193 return nil, err 194 } 195 extIndexes, extData = extIndexes[eh[fieldExtStringCount]:], extData[last:] 196 // read extended bool names 197 ti.ExtBoolNames, _, err = readStrings(extIndexes, extData, eh[fieldExtBoolCount]) 198 if err != nil { 199 return nil, err 200 } 201 extIndexes = extIndexes[eh[fieldExtBoolCount]:] 202 // read extended num names 203 ti.ExtNumNames, _, err = readStrings(extIndexes, extData, eh[fieldExtNumCount]) 204 if err != nil { 205 return nil, err 206 } 207 extIndexes = extIndexes[eh[fieldExtNumCount]:] 208 // read extended string names 209 ti.ExtStringNames, _, err = readStrings(extIndexes, extData, eh[fieldExtStringCount]) 210 if err != nil { 211 return nil, err 212 } 213 // extIndexes = extIndexes[eh[fieldExtStringCount]:] 214 return ti, nil 215} 216 217// Open reads the terminfo file name from the specified directory dir. 218func Open(dir, name string) (*Terminfo, error) { 219 var err error 220 var buf []byte 221 var filename string 222 for _, f := range []string{ 223 path.Join(dir, name[0:1], name), 224 path.Join(dir, strconv.FormatUint(uint64(name[0]), 16), name), 225 } { 226 buf, err = ioutil.ReadFile(f) 227 if err == nil { 228 filename = f 229 break 230 } 231 } 232 if buf == nil { 233 return nil, ErrFileNotFound 234 } 235 // decode 236 ti, err := Decode(buf) 237 if err != nil { 238 return nil, err 239 } 240 // save original file name 241 ti.File = filename 242 // add to cache 243 termCache.Lock() 244 for _, n := range ti.Names { 245 termCache.db[n] = ti 246 } 247 termCache.Unlock() 248 return ti, nil 249} 250 251// boolCaps returns all bool and extended capabilities using f to format the 252// index key. 253func (ti *Terminfo) boolCaps(f func(int) string, extended bool) map[string]bool { 254 m := make(map[string]bool, len(ti.Bools)+len(ti.ExtBools)) 255 if !extended { 256 for k, v := range ti.Bools { 257 m[f(k)] = v 258 } 259 } else { 260 for k, v := range ti.ExtBools { 261 m[string(ti.ExtBoolNames[k])] = v 262 } 263 } 264 return m 265} 266 267// BoolCaps returns all bool capabilities. 268func (ti *Terminfo) BoolCaps() map[string]bool { 269 return ti.boolCaps(BoolCapName, false) 270} 271 272// BoolCapsShort returns all bool capabilities, using the short name as the 273// index. 274func (ti *Terminfo) BoolCapsShort() map[string]bool { 275 return ti.boolCaps(BoolCapNameShort, false) 276} 277 278// ExtBoolCaps returns all extended bool capabilities. 279func (ti *Terminfo) ExtBoolCaps() map[string]bool { 280 return ti.boolCaps(BoolCapName, true) 281} 282 283// ExtBoolCapsShort returns all extended bool capabilities, using the short 284// name as the index. 285func (ti *Terminfo) ExtBoolCapsShort() map[string]bool { 286 return ti.boolCaps(BoolCapNameShort, true) 287} 288 289// numCaps returns all num and extended capabilities using f to format the 290// index key. 291func (ti *Terminfo) numCaps(f func(int) string, extended bool) map[string]int { 292 m := make(map[string]int, len(ti.Nums)+len(ti.ExtNums)) 293 if !extended { 294 for k, v := range ti.Nums { 295 m[f(k)] = v 296 } 297 } else { 298 for k, v := range ti.ExtNums { 299 m[string(ti.ExtNumNames[k])] = v 300 } 301 } 302 return m 303} 304 305// NumCaps returns all num capabilities. 306func (ti *Terminfo) NumCaps() map[string]int { 307 return ti.numCaps(NumCapName, false) 308} 309 310// NumCapsShort returns all num capabilities, using the short name as the 311// index. 312func (ti *Terminfo) NumCapsShort() map[string]int { 313 return ti.numCaps(NumCapNameShort, false) 314} 315 316// ExtNumCaps returns all extended num capabilities. 317func (ti *Terminfo) ExtNumCaps() map[string]int { 318 return ti.numCaps(NumCapName, true) 319} 320 321// ExtNumCapsShort returns all extended num capabilities, using the short 322// name as the index. 323func (ti *Terminfo) ExtNumCapsShort() map[string]int { 324 return ti.numCaps(NumCapNameShort, true) 325} 326 327// stringCaps returns all string and extended capabilities using f to format the 328// index key. 329func (ti *Terminfo) stringCaps(f func(int) string, extended bool) map[string][]byte { 330 m := make(map[string][]byte, len(ti.Strings)+len(ti.ExtStrings)) 331 if !extended { 332 for k, v := range ti.Strings { 333 m[f(k)] = v 334 } 335 } else { 336 for k, v := range ti.ExtStrings { 337 m[string(ti.ExtStringNames[k])] = v 338 } 339 } 340 return m 341} 342 343// StringCaps returns all string capabilities. 344func (ti *Terminfo) StringCaps() map[string][]byte { 345 return ti.stringCaps(StringCapName, false) 346} 347 348// StringCapsShort returns all string capabilities, using the short name as the 349// index. 350func (ti *Terminfo) StringCapsShort() map[string][]byte { 351 return ti.stringCaps(StringCapNameShort, false) 352} 353 354// ExtStringCaps returns all extended string capabilities. 355func (ti *Terminfo) ExtStringCaps() map[string][]byte { 356 return ti.stringCaps(StringCapName, true) 357} 358 359// ExtStringCapsShort returns all extended string capabilities, using the short 360// name as the index. 361func (ti *Terminfo) ExtStringCapsShort() map[string][]byte { 362 return ti.stringCaps(StringCapNameShort, true) 363} 364 365// Has determines if the bool cap i is present. 366func (ti *Terminfo) Has(i int) bool { 367 return ti.Bools[i] 368} 369 370// Num returns the num cap i, or -1 if not present. 371func (ti *Terminfo) Num(i int) int { 372 n, ok := ti.Nums[i] 373 if !ok { 374 return -1 375 } 376 return n 377} 378 379// Printf formats the string cap i, interpolating parameters v. 380func (ti *Terminfo) Printf(i int, v ...interface{}) string { 381 return Printf(ti.Strings[i], v...) 382} 383 384// Fprintf prints the string cap i to writer w, interpolating parameters v. 385func (ti *Terminfo) Fprintf(w io.Writer, i int, v ...interface{}) { 386 Fprintf(w, ti.Strings[i], v...) 387} 388 389// Color takes a foreground and background color and returns string that sets 390// them for this terminal. 391func (ti *Terminfo) Colorf(fg, bg int, str string) string { 392 maxColors := int(ti.Nums[MaxColors]) 393 // map bright colors to lower versions if the color table only holds 8. 394 if maxColors == 8 { 395 if fg > 7 && fg < 16 { 396 fg -= 8 397 } 398 if bg > 7 && bg < 16 { 399 bg -= 8 400 } 401 } 402 var s string 403 if maxColors > fg && fg >= 0 { 404 s += ti.Printf(SetAForeground, fg) 405 } 406 if maxColors > bg && bg >= 0 { 407 s += ti.Printf(SetABackground, bg) 408 } 409 return s + str + ti.Printf(ExitAttributeMode) 410} 411 412// Goto returns a string suitable for addressing the cursor at the given 413// row and column. The origin 0, 0 is in the upper left corner of the screen. 414func (ti *Terminfo) Goto(row, col int) string { 415 return Printf(ti.Strings[CursorAddress], row, col) 416} 417 418// Puts emits the string to the writer, but expands inline padding indications 419// (of the form $<[delay]> where [delay] is msec) to a suitable number of 420// padding characters (usually null bytes) based upon the supplied baud. At 421// high baud rates, more padding characters will be inserted. 422/*func (ti *Terminfo) Puts(w io.Writer, s string, lines, baud int) (int, error) { 423 var err error 424 for { 425 start := strings.Index(s, "$<") 426 if start == -1 { 427 // most strings don't need padding, which is good news! 428 return io.WriteString(w, s) 429 } 430 end := strings.Index(s, ">") 431 if end == -1 { 432 // unterminated... just emit bytes unadulterated. 433 return io.WriteString(w, "$<"+s) 434 } 435 var c int 436 c, err = io.WriteString(w, s[:start]) 437 if err != nil { 438 return n + c, err 439 } 440 n += c 441 s = s[start+2:] 442 val := s[:end] 443 s = s[end+1:] 444 var ms int 445 var dot, mandatory, asterisk bool 446 unit := 1000 447 for _, ch := range val { 448 switch { 449 case ch >= '0' && ch <= '9': 450 ms = (ms * 10) + int(ch-'0') 451 if dot { 452 unit *= 10 453 } 454 case ch == '.' && !dot: 455 dot = true 456 case ch == '*' && !asterisk: 457 ms *= lines 458 asterisk = true 459 case ch == '/': 460 mandatory = true 461 default: 462 break 463 } 464 } 465 z, pad := ((baud/8)/unit)*ms, ti.Strings[PadChar] 466 b := make([]byte, len(pad)*z) 467 for bp := copy(b, pad); bp < len(b); bp *= 2 { 468 copy(b[bp:], b[:bp]) 469 } 470 if (!ti.Bools[XonXoff] && baud > int(ti.Nums[PaddingBaudRate])) || mandatory { 471 c, err = w.Write(b) 472 if err != nil { 473 return n + c, err 474 } 475 n += c 476 } 477 } 478 return n, nil 479}*/