Bump github.com/gookit/config/v2 from 2.1.8 to 2.2.2

Bumps [github.com/gookit/config/v2](https://github.com/gookit/config) from 2.1.8 to 2.2.2.
- [Release notes](https://github.com/gookit/config/releases)
- [Commits](https://github.com/gookit/config/compare/v2.1.8...v2.2.2)

---
updated-dependencies:
- dependency-name: github.com/gookit/config/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2023-06-13 06:43:49 +00:00
committed by Ralf Haferkamp
parent 03045c5ccc
commit 5ebc596352
190 changed files with 18973 additions and 4170 deletions
-2
View File
@@ -1,7 +1,5 @@
package terminfo
//go:generate go run gen.go
// BoolCapName returns the bool capability name.
func BoolCapName(i int) string {
return boolCapNames[2*i]
+1 -499
View File
File diff suppressed because it is too large Load Diff
-3
View File
@@ -70,14 +70,12 @@ func ColorLevelFromEnv() (ColorLevel, error) {
}
return ColorLevelHundreds, nil
}
// otherwise determine from TERM's max_colors capability
if term := os.Getenv("TERM"); term != "" {
ti, err := Load(term)
if err != nil {
return ColorLevelNone, err
}
v, ok := ti.Nums[MaxColors]
switch {
case !ok || v <= 16:
@@ -86,6 +84,5 @@ func ColorLevelFromEnv() (ColorLevel, error) {
return ColorLevelHundreds, nil
}
}
return ColorLevelBasic, nil
}
@@ -7,12 +7,11 @@ import (
const (
// maxFileLength is the max file length.
maxFileLength = 4096
// magic is the file magic for terminfo files.
magic = 0432
// magicExtended is the file magic for terminfo files with the extended number format.
magicExtended = 01036
magic = 0o432
// magicExtended is the file magic for terminfo files with the extended
// number format.
magicExtended = 0o1036
)
// header fields.
@@ -99,12 +98,12 @@ func readStrings(idx []int, buf []byte, n int) (map[int][]byte, int, error) {
type decoder struct {
buf []byte
pos int
len int
n int
}
// readBytes reads the next n bytes of buf, incrementing pos by n.
func (d *decoder) readBytes(n int) ([]byte, error) {
if d.len < d.pos+n {
if d.n < d.pos+n {
return nil, ErrUnexpectedFileEnd
}
n, d.pos = d.pos, d.pos+n
@@ -115,15 +114,12 @@ func (d *decoder) readBytes(n int) ([]byte, error) {
func (d *decoder) readInts(n, w int) ([]int, error) {
w /= 8
l := n * w
buf, err := d.readBytes(l)
if err != nil {
return nil, err
}
// align
d.pos += d.pos % 2
z := make([]int, n)
for i, j := 0, 0; i < l; i, j = i+w, j+1 {
switch w {
@@ -135,7 +131,6 @@ func (d *decoder) readInts(n, w int) ([]int, error) {
z[j] = int(buf[i+3])<<24 | int(buf[i+2])<<16 | int(buf[i+1])<<8 | int(buf[i])
}
}
return z, nil
}
@@ -145,7 +140,6 @@ func (d *decoder) readBools(n int) (map[int]bool, map[int]bool, error) {
if err != nil {
return nil, nil, err
}
// process
bools, boolsM := make(map[int]bool), make(map[int]bool)
for i, b := range buf {
@@ -154,7 +148,6 @@ func (d *decoder) readBools(n int) (map[int]bool, map[int]bool, error) {
boolsM[i] = true
}
}
return bools, boolsM, nil
}
@@ -164,7 +157,6 @@ func (d *decoder) readNums(n, w int) (map[int]int, map[int]bool, error) {
if err != nil {
return nil, nil, err
}
// process
nums, numsM := make(map[int]int), make(map[int]bool)
for i := 0; i < n; i++ {
@@ -173,7 +165,6 @@ func (d *decoder) readNums(n, w int) (map[int]int, map[int]bool, error) {
numsM[i] = true
}
}
return nums, numsM, nil
}
@@ -184,16 +175,13 @@ func (d *decoder) readStringTable(n, sz int) ([][]byte, []int, error) {
if err != nil {
return nil, nil, err
}
// read string data table
data, err := d.readBytes(sz)
if err != nil {
return nil, nil, err
}
// align
d.pos += d.pos % 2
// process
s := make([][]byte, n)
var m []int
@@ -209,7 +197,6 @@ func (d *decoder) readStringTable(n, sz int) ([][]byte, []int, error) {
}
}
}
return s, m, nil
}
@@ -220,7 +207,6 @@ func (d *decoder) readStrings(n, sz int) (map[int][]byte, map[int]bool, error) {
if err != nil {
return nil, nil, err
}
strs := make(map[int][]byte)
for k, v := range s {
if k == AcsChars {
@@ -228,39 +214,32 @@ func (d *decoder) readStrings(n, sz int) (map[int][]byte, map[int]bool, error) {
}
strs[k] = v
}
strsM := make(map[int]bool, len(m))
for _, k := range m {
strsM[k] = true
}
return strs, strsM, nil
}
// canonicalizeAscChars reorders chars to be unique, in order.
//
// see repair_ascc in ncurses-6.0/progs/dump_entry.c
// see repair_ascc in ncurses-6.3/progs/dump_entry.c
func canonicalizeAscChars(z []byte) []byte {
var c chars
var c []byte
enc := make(map[byte]byte, len(z)/2)
for i := 0; i < len(z); i += 2 {
if _, ok := enc[z[i]]; !ok {
a, b := z[i], z[i+1]
//log.Printf(">>> a: %d %c, b: %d %c", a, a, b, b)
// log.Printf(">>> a: %d %c, b: %d %c", a, a, b, b)
c, enc[a] = append(c, b), b
}
}
sort.Sort(c)
sort.Slice(c, func(i, j int) bool {
return c[i] < c[j]
})
r := make([]byte, 2*len(c))
for i := 0; i < len(c); i++ {
r[i*2], r[i*2+1] = c[i], enc[c[i]]
}
return r
}
type chars []byte
func (c chars) Len() int { return len(c) }
func (c chars) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c chars) Less(i, j int) bool { return c[i] < c[j] }
-8
View File
@@ -23,34 +23,27 @@ func Load(name string) (*Terminfo, error) {
if name == "" {
return nil, ErrEmptyTermName
}
termCache.RLock()
ti, ok := termCache.db[name]
termCache.RUnlock()
if ok {
return ti, nil
}
var checkDirs []string
// check $TERMINFO
if dir := os.Getenv("TERMINFO"); dir != "" {
checkDirs = append(checkDirs, dir)
}
// check $HOME/.terminfo
u, err := user.Current()
if err != nil {
return nil, err
}
checkDirs = append(checkDirs, path.Join(u.HomeDir, ".terminfo"))
// check $TERMINFO_DIRS
if dirs := os.Getenv("TERMINFO_DIRS"); dirs != "" {
checkDirs = append(checkDirs, strings.Split(dirs, ":")...)
}
// check fallback directories
checkDirs = append(checkDirs, "/etc/terminfo", "/lib/terminfo", "/usr/share/terminfo")
for _, dir := range checkDirs {
@@ -61,7 +54,6 @@ func Load(name string) (*Terminfo, error) {
return ti, nil
}
}
return nil, ErrDatabaseDirectoryNotFound
}
-85
View File
@@ -13,25 +13,18 @@ import (
type parametizer struct {
// z is the string to parameterize
z []byte
// pos is the current position in s.
pos int
// nest is the current nest level.
nest int
// s is the variable stack.
s stack
// skipElse keeps the state of skipping else.
skipElse bool
// buf is the result buffer.
buf *bytes.Buffer
// params are the parameters to interpolate.
params [9]interface{}
// vars are dynamic variables.
vars [26]interface{}
}
@@ -54,19 +47,15 @@ var parametizerPool = sync.Pool{
func newParametizer(z []byte) *parametizer {
p := parametizerPool.Get().(*parametizer)
p.z = z
return p
}
// reset resets the parametizer.
func (p *parametizer) reset() {
p.pos, p.nest = 0, 0
p.s.reset()
p.buf.Reset()
p.params, p.vars = [9]interface{}{}, [26]interface{}{}
parametizerPool.Put(p)
}
@@ -106,13 +95,11 @@ func (p *parametizer) scanTextFn() stateFn {
p.writeFrom(ppos)
return nil
}
if ch == '%' {
p.writeFrom(ppos)
p.pos++
return p.scanCodeFn
}
p.pos++
}
}
@@ -122,11 +109,9 @@ func (p *parametizer) scanCodeFn() stateFn {
if err != nil {
return nil
}
switch ch {
case '%':
p.buf.WriteByte('%')
case ':':
// this character is used to avoid interpreting "%-" and "%+" as operators.
// the next character is where the format really begins.
@@ -136,71 +121,52 @@ func (p *parametizer) scanCodeFn() stateFn {
return nil
}
return p.scanFormatFn
case '#', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':
return p.scanFormatFn
case 'o':
p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 8))
case 'd':
p.buf.WriteString(strconv.Itoa(p.s.popInt()))
case 'x':
p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 16))
case 'X':
p.buf.WriteString(strings.ToUpper(strconv.FormatInt(int64(p.s.popInt()), 16)))
case 's':
p.buf.WriteString(p.s.popString())
case 'c':
p.buf.WriteByte(p.s.popByte())
case 'p':
p.pos++
return p.pushParamFn
case 'P':
p.pos++
return p.setDsVarFn
case 'g':
p.pos++
return p.getDsVarFn
case '\'':
p.pos++
ch, err = p.peek()
if err != nil {
return nil
}
p.s.push(ch)
// skip the '\''
p.pos++
case '{':
p.pos++
return p.pushIntfn
case 'l':
p.s.push(len(p.s.popString()))
case '+':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai + bi)
case '-':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai - bi)
case '*':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai * bi)
case '/':
bi, ai := p.s.popInt(), p.s.popInt()
if bi != 0 {
@@ -208,7 +174,6 @@ func (p *parametizer) scanCodeFn() stateFn {
} else {
p.s.push(0)
}
case 'm':
bi, ai := p.s.popInt(), p.s.popInt()
if bi != 0 {
@@ -216,101 +181,77 @@ func (p *parametizer) scanCodeFn() stateFn {
} else {
p.s.push(0)
}
case '&':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai & bi)
case '|':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai | bi)
case '^':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai ^ bi)
case '=':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai == bi)
case '>':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai > bi)
case '<':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai < bi)
case 'A':
bi, ai := p.s.popBool(), p.s.popBool()
p.s.push(ai && bi)
case 'O':
bi, ai := p.s.popBool(), p.s.popBool()
p.s.push(ai || bi)
case '!':
p.s.push(!p.s.popBool())
case '~':
p.s.push(^p.s.popInt())
case 'i':
for i := range p.params[:2] {
if n, ok := p.params[i].(int); ok {
p.params[i] = n + 1
}
}
case '?', ';':
case 't':
return p.scanThenFn
case 'e':
p.skipElse = true
return p.skipTextFn
}
p.pos++
return p.scanTextFn
}
func (p *parametizer) scanFormatFn() stateFn {
// the character was already read, so no need to check the error.
ch, _ := p.peek()
// 6 should be the maximum length of a format string, for example "%:-9.9d".
f := []byte{'%', ch, 0, 0, 0, 0}
var err error
for {
p.pos++
ch, err = p.peek()
if err != nil {
return nil
}
f = append(f, ch)
switch ch {
case 'o', 'd', 'x', 'X':
fmt.Fprintf(p.buf, string(f), p.s.popInt())
break
case 's':
fmt.Fprintf(p.buf, string(f), p.s.popString())
break
case 'c':
fmt.Fprintf(p.buf, string(f), p.s.popByte())
break
}
}
p.pos++
return p.scanTextFn
}
@@ -319,16 +260,13 @@ func (p *parametizer) pushParamFn() stateFn {
if err != nil {
return nil
}
if ai := int(ch - '1'); ai >= 0 && ai < len(p.params) {
p.s.push(p.params[ai])
} else {
p.s.push(0)
}
// skip the '}'
p.pos++
return p.scanTextFn
}
@@ -337,7 +275,6 @@ func (p *parametizer) setDsVarFn() stateFn {
if err != nil {
return nil
}
if ch >= 'A' && ch <= 'Z' {
staticVars.Lock()
staticVars.vars[int(ch-'A')] = p.s.pop()
@@ -345,7 +282,6 @@ func (p *parametizer) setDsVarFn() stateFn {
} else if ch >= 'a' && ch <= 'z' {
p.vars[int(ch-'a')] = p.s.pop()
}
p.pos++
return p.scanTextFn
}
@@ -355,20 +291,16 @@ func (p *parametizer) getDsVarFn() stateFn {
if err != nil {
return nil
}
var a byte
if ch >= 'A' && ch <= 'Z' {
a = 'A'
} else if ch >= 'a' && ch <= 'z' {
a = 'a'
}
staticVars.Lock()
p.s.push(staticVars.vars[int(ch-a)])
staticVars.Unlock()
p.pos++
return p.scanTextFn
}
@@ -379,26 +311,21 @@ func (p *parametizer) pushIntfn() stateFn {
if err != nil {
return nil
}
p.pos++
if ch < '0' || ch > '9' {
p.s.push(ai)
return p.scanTextFn
}
ai = (ai * 10) + int(ch-'0')
}
}
func (p *parametizer) scanThenFn() stateFn {
p.pos++
if p.s.popBool() {
return p.scanTextFn
}
p.skipElse = false
return p.skipTextFn
}
@@ -408,17 +335,14 @@ func (p *parametizer) skipTextFn() stateFn {
if err != nil {
return nil
}
p.pos++
if ch == '%' {
break
}
}
if p.skipElse {
return p.skipElseFn
}
return p.skipThenFn
}
@@ -427,7 +351,6 @@ func (p *parametizer) skipThenFn() stateFn {
if err != nil {
return nil
}
p.pos++
switch ch {
case ';':
@@ -435,16 +358,13 @@ func (p *parametizer) skipThenFn() stateFn {
return p.scanTextFn
}
p.nest--
case '?':
p.nest++
case 'e':
if p.nest == 0 {
return p.scanTextFn
}
}
return p.skipTextFn
}
@@ -453,7 +373,6 @@ func (p *parametizer) skipElseFn() stateFn {
if err != nil {
return nil
}
p.pos++
switch ch {
case ';':
@@ -461,11 +380,9 @@ func (p *parametizer) skipElseFn() stateFn {
return p.scanTextFn
}
p.nest--
case '?':
p.nest++
}
return p.skipTextFn
}
@@ -473,13 +390,11 @@ func (p *parametizer) skipElseFn() stateFn {
func Printf(z []byte, params ...interface{}) string {
p := newParametizer(z)
defer p.reset()
// make sure we always have 9 parameters -- makes it easier
// later to skip checks and its faster
for i := 0; i < len(p.params) && i < len(params); i++ {
p.params[i] = params[i]
}
return p.exec()
}
+12 -71
View File
@@ -1,6 +1,8 @@
// Package terminfo implements reading terminfo files in pure go.
package terminfo
//go:generate go run gen.go
import (
"io"
"io/ioutil"
@@ -20,34 +22,24 @@ func (err Error) Error() string {
const (
// ErrInvalidFileSize is the invalid file size error.
ErrInvalidFileSize Error = "invalid file size"
// ErrUnexpectedFileEnd is the unexpected file end error.
ErrUnexpectedFileEnd Error = "unexpected file end"
// ErrInvalidStringTable is the invalid string table error.
ErrInvalidStringTable Error = "invalid string table"
// ErrInvalidMagic is the invalid magic error.
ErrInvalidMagic Error = "invalid magic"
// ErrInvalidHeader is the invalid header error.
ErrInvalidHeader Error = "invalid header"
// ErrInvalidNames is the invalid names error.
ErrInvalidNames Error = "invalid names"
// ErrInvalidExtendedHeader is the invalid extended header error.
ErrInvalidExtendedHeader Error = "invalid extended header"
// ErrEmptyTermName is the empty term name error.
ErrEmptyTermName Error = "empty term name"
// ErrDatabaseDirectoryNotFound is the database directory not found error.
ErrDatabaseDirectoryNotFound Error = "database directory not found"
// ErrFileNotFound is the file not found error.
ErrFileNotFound Error = "file not found"
// ErrInvalidTermProgramVersion is the invalid TERM_PROGRAM_VERSION error.
ErrInvalidTermProgramVersion Error = "invalid TERM_PROGRAM_VERSION"
)
@@ -56,43 +48,30 @@ const (
type Terminfo struct {
// File is the original source file.
File string
// Names are the provided cap names.
Names []string
// Bools are the bool capabilities.
Bools map[int]bool
// BoolsM are the missing bool capabilities.
BoolsM map[int]bool
// Nums are the num capabilities.
Nums map[int]int
// NumsM are the missing num capabilities.
NumsM map[int]bool
// Strings are the string capabilities.
Strings map[int][]byte
// StringsM are the missing string capabilities.
StringsM map[int]bool
// ExtBools are the extended bool capabilities.
ExtBools map[int]bool
// ExtBoolsNames is the map of extended bool capabilities to their index.
ExtBoolNames map[int][]byte
// ExtNums are the extended num capabilities.
ExtNums map[int]int
// ExtNumsNames is the map of extended num capabilities to their index.
ExtNumNames map[int][]byte
// ExtStrings are the extended string capabilities.
ExtStrings map[int][]byte
// ExtStringsNames is the map of extended string capabilities to their index.
ExtStringNames map[int][]byte
}
@@ -100,75 +79,63 @@ type Terminfo struct {
// Decode decodes the terminfo data contained in buf.
func Decode(buf []byte) (*Terminfo, error) {
var err error
// check max file length
if len(buf) >= maxFileLength {
return nil, ErrInvalidFileSize
}
d := &decoder{
buf: buf,
len: len(buf),
n: len(buf),
}
// read header
h, err := d.readInts(6, 16)
if err != nil {
return nil, err
}
var numWidth int
// check magic
if h[fieldMagic] == magic {
switch {
case h[fieldMagic] == magic:
numWidth = 16
} else if h[fieldMagic] == magicExtended {
case h[fieldMagic] == magicExtended:
numWidth = 32
} else {
default:
return nil, ErrInvalidMagic
}
// check header
if hasInvalidCaps(h) {
return nil, ErrInvalidHeader
}
// check remaining length
if d.len-d.pos < capLength(h) {
if d.n-d.pos < capLength(h) {
return nil, ErrUnexpectedFileEnd
}
// read names
names, err := d.readBytes(h[fieldNameSize])
if err != nil {
return nil, err
}
// check name is terminated properly
i := findNull(names, 0)
if i == -1 {
return nil, ErrInvalidNames
}
names = names[:i]
// read bool caps
bools, boolsM, err := d.readBools(h[fieldBoolCount])
if err != nil {
return nil, err
}
// read num caps
nums, numsM, err := d.readNums(h[fieldNumCount], numWidth)
if err != nil {
return nil, err
}
// read string caps
strs, strsM, err := d.readStrings(h[fieldStringCount], h[fieldTableSize])
if err != nil {
return nil, err
}
ti := &Terminfo{
Names: strings.Split(string(names), "|"),
Bools: bools,
@@ -178,57 +145,47 @@ func Decode(buf []byte) (*Terminfo, error) {
Strings: strs,
StringsM: strsM,
}
// at the end of file, so no extended caps
if d.pos >= d.len {
if d.pos >= d.n {
return ti, nil
}
// decode extended header
eh, err := d.readInts(5, 16)
if err != nil {
return nil, err
}
// check extended offset field
if hasInvalidExtOffset(eh) {
return nil, ErrInvalidExtendedHeader
}
// check extended cap lengths
if d.len-d.pos != extCapLength(eh, numWidth) {
if d.n-d.pos != extCapLength(eh, numWidth) {
return nil, ErrInvalidExtendedHeader
}
// read extended bool caps
ti.ExtBools, _, err = d.readBools(eh[fieldExtBoolCount])
if err != nil {
return nil, err
}
// read extended num caps
ti.ExtNums, _, err = d.readNums(eh[fieldExtNumCount], numWidth)
if err != nil {
return nil, err
}
// read extended string data table indexes
extIndexes, err := d.readInts(eh[fieldExtOffsetCount], 16)
if err != nil {
return nil, err
}
// read string data table
extData, err := d.readBytes(eh[fieldExtTableSize])
if err != nil {
return nil, err
}
// precautionary check that exactly at end of file
if d.pos != d.len {
if d.pos != d.n {
return nil, ErrUnexpectedFileEnd
}
var last int
// read extended string caps
ti.ExtStrings, last, err = readStrings(extIndexes, extData, eh[fieldExtStringCount])
@@ -236,28 +193,24 @@ func Decode(buf []byte) (*Terminfo, error) {
return nil, err
}
extIndexes, extData = extIndexes[eh[fieldExtStringCount]:], extData[last:]
// read extended bool names
ti.ExtBoolNames, _, err = readStrings(extIndexes, extData, eh[fieldExtBoolCount])
if err != nil {
return nil, err
}
extIndexes = extIndexes[eh[fieldExtBoolCount]:]
// read extended num names
ti.ExtNumNames, _, err = readStrings(extIndexes, extData, eh[fieldExtNumCount])
if err != nil {
return nil, err
}
extIndexes = extIndexes[eh[fieldExtNumCount]:]
// read extended string names
ti.ExtStringNames, _, err = readStrings(extIndexes, extData, eh[fieldExtStringCount])
if err != nil {
return nil, err
}
//extIndexes = extIndexes[eh[fieldExtStringCount]:]
// extIndexes = extIndexes[eh[fieldExtStringCount]:]
return ti, nil
}
@@ -279,23 +232,19 @@ func Open(dir, name string) (*Terminfo, error) {
if buf == nil {
return nil, ErrFileNotFound
}
// decode
ti, err := Decode(buf)
if err != nil {
return nil, err
}
// save original file name
ti.File = filename
// add to cache
termCache.Lock()
for _, n := range ti.Names {
termCache.db[n] = ti
}
termCache.Unlock()
return ti, nil
}
@@ -441,7 +390,6 @@ func (ti *Terminfo) Fprintf(w io.Writer, i int, v ...interface{}) {
// them for this terminal.
func (ti *Terminfo) Colorf(fg, bg int, str string) string {
maxColors := int(ti.Nums[MaxColors])
// map bright colors to lower versions if the color table only holds 8.
if maxColors == 8 {
if fg > 7 && fg < 16 {
@@ -451,7 +399,6 @@ func (ti *Terminfo) Colorf(fg, bg int, str string) string {
bg -= 8
}
}
var s string
if maxColors > fg && fg >= 0 {
s += ti.Printf(SetAForeground, fg)
@@ -480,20 +427,17 @@ func (ti *Terminfo) Goto(row, col int) string {
// most strings don't need padding, which is good news!
return io.WriteString(w, s)
}
end := strings.Index(s, ">")
if end == -1 {
// unterminated... just emit bytes unadulterated.
return io.WriteString(w, "$<"+s)
}
var c int
c, err = io.WriteString(w, s[:start])
if err != nil {
return n + c, err
}
n += c
s = s[start+2:]
val := s[:end]
s = s[end+1:]
@@ -518,13 +462,11 @@ func (ti *Terminfo) Goto(row, col int) string {
break
}
}
z, pad := ((baud/8)/unit)*ms, ti.Strings[PadChar]
b := make([]byte, len(pad)*z)
for bp := copy(b, pad); bp < len(b); bp *= 2 {
copy(b[bp:], b[:bp])
}
if (!ti.Bools[XonXoff] && baud > int(ti.Nums[PaddingBaudRate])) || mandatory {
c, err = w.Write(b)
if err != nil {
@@ -533,6 +475,5 @@ func (ti *Terminfo) Goto(row, col int) string {
n += c
}
}
return n, nil
}*/