Compare commits

...

1 Commits

Author SHA1 Message Date
de20d8f834
implement text/encoding and split packages 2024-08-25 19:55:43 +02:00
30 changed files with 4339 additions and 1300 deletions

View File

@ -0,0 +1,84 @@
package charmap
import (
"maps"
mnemonic "git.sdf.org/CRThaze/vtTools/encoding/decMSCMnemonics"
)
var (
decMSCControl0 = map[rune]byte{
'\u0000': mnemonic.NUL,
'\u0001': mnemonic.SOH,
'\u0002': mnemonic.STX,
'\u0003': mnemonic.ETX,
'\u0004': mnemonic.EOT,
'\u0005': mnemonic.ENQ,
'\u0006': mnemonic.ACK,
'\u0007': mnemonic.BEL,
'\u0008': mnemonic.BS,
'\u0009': mnemonic.HT,
'\u000a': mnemonic.LF,
'\u000b': mnemonic.VT,
'\u000c': mnemonic.FF,
'\u000d': mnemonic.CR,
'\u000e': mnemonic.SO,
'\u000f': mnemonic.SI,
'\u0010': mnemonic.DLE,
'\u0011': mnemonic.DC1,
'\u0012': mnemonic.DC2,
'\u0013': mnemonic.DC3,
'\u0014': mnemonic.DC4,
'\u0015': mnemonic.NAK,
'\u0016': mnemonic.SYN,
'\u0017': mnemonic.ETB,
'\u0018': mnemonic.CAN,
'\u0019': mnemonic.EM,
'\u001a': mnemonic.SUB,
'\u001b': mnemonic.ESC,
'\u001c': mnemonic.FS,
'\u001d': mnemonic.GS,
'\u001e': mnemonic.RS,
'\u001f': mnemonic.US,
}
decMSCControl1 = map[rune]byte{
'\u0080': mnemonic.SUB, // PAD
'\u0081': mnemonic.SUB, // HOP
'\u0082': mnemonic.SUB, // BPH
'\u0083': mnemonic.SUB, // NBH
'\u0084': mnemonic.IND,
'\u0085': mnemonic.NEL,
'\u0086': mnemonic.SSA,
'\u0087': mnemonic.ESA,
'\u0088': mnemonic.HTS,
'\u0089': mnemonic.HTJ,
'\u008a': mnemonic.VTS,
'\u008b': mnemonic.PLD,
'\u008c': mnemonic.PLU,
'\u008d': mnemonic.RI,
'\u008e': mnemonic.SS2,
'\u008f': mnemonic.SS3,
'\u0090': mnemonic.DCS,
'\u0091': mnemonic.PU1,
'\u0092': mnemonic.PU2,
'\u0093': mnemonic.STS,
'\u0094': mnemonic.CCH,
'\u0095': mnemonic.MW,
'\u0096': mnemonic.SPA,
'\u0097': mnemonic.EPA,
'\u0098': mnemonic.SUB, // SOS
'\u0099': mnemonic.SUB, // SGCI
'\u009a': mnemonic.SUB, // CSI
'\u009b': mnemonic.CSI,
'\u009c': mnemonic.ST,
'\u009d': mnemonic.OSC,
'\u009e': mnemonic.PM,
'\u009f': mnemonic.APC,
}
decMSCControlFull = map[rune]byte{}
)
func init() {
maps.Copy(decMSCControlFull, decMSCControl0)
maps.Copy(decMSCControlFull, decMSCControl1)
}

View File

@ -0,0 +1,53 @@
package charmap
var (
decMSCASCII = GlyphTable{
{C(' '), C('0'), C('@'), C('P'), C('`'), C('p')},
{C('!'), C('1'), C('A'), C('Q'), C('a'), C('q')},
{C('"'), C('2'), C('B'), C('R'), C('b'), C('r')},
{C('#'), C('3'), C('C'), C('S'), C('c'), C('s')},
{C('$'), C('4'), C('D'), C('T'), C('d'), C('t')},
{C('%'), C('5'), C('E'), C('U'), C('e'), C('u')},
{C('&'), C('6'), C('F'), C('V'), C('f'), C('v')},
{C('\''), C('7'), C('G'), C('W'), C('g'), C('w')},
{C('('), C('8'), C('H'), C('X'), C('h'), C('x')},
{C(')'), C('9'), C('I'), C('Y'), C('i'), C('y')},
{C('*'), C(':'), C('J'), C('Z'), C('j'), C('z')},
{C('+'), C(';'), C('K'), C('['), C('k'), C('{')},
{C(','), C('<'), C('L'), C('\\'), C('l'), C('|')},
{C('-'), C('='), C('M'), C(']'), C('m'), C('}')},
{C('.'), C('>'), C('N'), C('^'), C('n'), C('~')},
{C('/'), C('?'), C('O'), C('_'), C('o'), C('\u007F')},
}
decMSCSupplemental = GlyphTable{
{N(nil), C('°'), C('À'), N(nil), C('à'), N(nil)},
{C('¡'), C('±'), C('Á'), C('Ñ'), C('á'), C('ñ')},
{C('¢'), C('²'), C('Â'), C('Ò'), C('â'), C('ò')},
{C('£'), C('³'), C('Ã'), C('Ó'), C('ã'), C('ó')},
{N(nil), N(nil), C('Ä'), C('Ô'), C('ä'), C('ô')},
{C('¥'), C('µ'), C('Å'), C('Õ'), C('å'), C('õ')},
{N(nil), C('¶'), C('Æ'), C('Ö'), C('æ'), C('ö')},
{C('§'), C('·'), C('Ç'), C('Œ'), C('ç'), C('œ')},
{C('¤'), N(nil), C('È'), C('Ø'), C('è'), C('ø')},
{C('©'), C('¹'), C('É'), C('Ù'), C('é'), C('ù')},
{C('ª'), C('º'), C('Ê'), C('Ú'), C('ê'), C('ú')},
{C('«'), C('»'), C('Ë'), C('Û'), C('ë'), C('û')},
{N(nil), C('¼'), C('Ì'), C('Ü'), C('ì'), C('ü')},
{N(nil), C('½'), C('Í'), C('Ÿ'), C('í'), C('ÿ')},
{N(nil), N(nil), C('Î'), N(nil), C('î'), N(nil)},
{N(nil), C('¿'), C('Ï'), C('ß'), C('ï'), N(nil)},
}
)
var (
DEC_MSC = NewExtendedCodepage(
CharacterSetFromTable(decMSCASCII),
CharacterSetFromTable(decMSCSupplemental),
)
DEC_MSC_ASCII = NewDynamicCodepage(
CharacterSetFromTable(decMSCASCII),
)
DEC_MSC_Supplemental = NewDynamicCodepage(
CharacterSetFromTable(decMSCSupplemental),
)
)

249
encoding/charmap/decNRC.go Normal file
View File

@ -0,0 +1,249 @@
package charmap
var (
// TODO: Correct to Match docs
decNRCBritish = GlyphTable{
{C(' '), C('0'), C('@'), C('P'), C('`'), C('p')},
{C('!'), C('1'), C('A'), C('Q'), C('a'), C('q')},
{C('"'), C('2'), C('B'), C('R'), C('b'), C('r')},
{C('#'), C('3'), C('C'), C('S'), C('c'), C('s')},
{C('$'), C('4'), C('D'), C('T'), C('d'), C('t')},
{C('%'), C('5'), C('E'), C('U'), C('e'), C('u')},
{C('&'), C('6'), C('F'), C('V'), C('f'), C('v')},
{C('\''), C('7'), C('G'), C('W'), C('g'), C('w')},
{C('('), C('8'), C('H'), C('X'), C('h'), C('x')},
{C(')'), C('9'), C('I'), C('Y'), C('i'), C('y')},
{C('*'), C(':'), C('J'), C('Z'), C('j'), C('z')},
{C('+'), C(';'), C('K'), C('['), C('k'), C('{')},
{C(','), C('<'), C('L'), C('\\'), C('l'), C('|')},
{C('-'), C('='), C('M'), C(']'), C('m'), C('}')},
{C('.'), C('>'), C('N'), C('^'), C('n'), C('~')},
{C('/'), C('?'), C('O'), C('_'), C('o'), C('\u007F')},
}
// TODO: Correct to Match docs
decNRCDutch = GlyphTable{
{C(' '), C('0'), C('@'), C('P'), C('`'), C('p')},
{C('!'), C('1'), C('A'), C('Q'), C('a'), C('q')},
{C('"'), C('2'), C('B'), C('R'), C('b'), C('r')},
{C('#'), C('3'), C('C'), C('S'), C('c'), C('s')},
{C('$'), C('4'), C('D'), C('T'), C('d'), C('t')},
{C('%'), C('5'), C('E'), C('U'), C('e'), C('u')},
{C('&'), C('6'), C('F'), C('V'), C('f'), C('v')},
{C('\''), C('7'), C('G'), C('W'), C('g'), C('w')},
{C('('), C('8'), C('H'), C('X'), C('h'), C('x')},
{C(')'), C('9'), C('I'), C('Y'), C('i'), C('y')},
{C('*'), C(':'), C('J'), C('Z'), C('j'), C('z')},
{C('+'), C(';'), C('K'), C('['), C('k'), C('{')},
{C(','), C('<'), C('L'), C('\\'), C('l'), C('|')},
{C('-'), C('='), C('M'), C(']'), C('m'), C('}')},
{C('.'), C('>'), C('N'), C('^'), C('n'), C('~')},
{C('/'), C('?'), C('O'), C('_'), C('o'), C('\u007F')},
}
// TODO: Correct to Match docs
decNRCFinnish = GlyphTable{
{C(' '), C('0'), C('@'), C('P'), C('`'), C('p')},
{C('!'), C('1'), C('A'), C('Q'), C('a'), C('q')},
{C('"'), C('2'), C('B'), C('R'), C('b'), C('r')},
{C('#'), C('3'), C('C'), C('S'), C('c'), C('s')},
{C('$'), C('4'), C('D'), C('T'), C('d'), C('t')},
{C('%'), C('5'), C('E'), C('U'), C('e'), C('u')},
{C('&'), C('6'), C('F'), C('V'), C('f'), C('v')},
{C('\''), C('7'), C('G'), C('W'), C('g'), C('w')},
{C('('), C('8'), C('H'), C('X'), C('h'), C('x')},
{C(')'), C('9'), C('I'), C('Y'), C('i'), C('y')},
{C('*'), C(':'), C('J'), C('Z'), C('j'), C('z')},
{C('+'), C(';'), C('K'), C('['), C('k'), C('{')},
{C(','), C('<'), C('L'), C('\\'), C('l'), C('|')},
{C('-'), C('='), C('M'), C(']'), C('m'), C('}')},
{C('.'), C('>'), C('N'), C('^'), C('n'), C('~')},
{C('/'), C('?'), C('O'), C('_'), C('o'), C('\u007F')},
}
// TODO: Correct to Match docs
decNRCFrench = GlyphTable{
{C(' '), C('0'), C('@'), C('P'), C('`'), C('p')},
{C('!'), C('1'), C('A'), C('Q'), C('a'), C('q')},
{C('"'), C('2'), C('B'), C('R'), C('b'), C('r')},
{C('#'), C('3'), C('C'), C('S'), C('c'), C('s')},
{C('$'), C('4'), C('D'), C('T'), C('d'), C('t')},
{C('%'), C('5'), C('E'), C('U'), C('e'), C('u')},
{C('&'), C('6'), C('F'), C('V'), C('f'), C('v')},
{C('\''), C('7'), C('G'), C('W'), C('g'), C('w')},
{C('('), C('8'), C('H'), C('X'), C('h'), C('x')},
{C(')'), C('9'), C('I'), C('Y'), C('i'), C('y')},
{C('*'), C(':'), C('J'), C('Z'), C('j'), C('z')},
{C('+'), C(';'), C('K'), C('['), C('k'), C('{')},
{C(','), C('<'), C('L'), C('\\'), C('l'), C('|')},
{C('-'), C('='), C('M'), C(']'), C('m'), C('}')},
{C('.'), C('>'), C('N'), C('^'), C('n'), C('~')},
{C('/'), C('?'), C('O'), C('_'), C('o'), C('\u007F')},
}
// TODO: Correct to Match docs
decNRCFrenchCanadian = GlyphTable{
{C(' '), C('0'), C('@'), C('P'), C('`'), C('p')},
{C('!'), C('1'), C('A'), C('Q'), C('a'), C('q')},
{C('"'), C('2'), C('B'), C('R'), C('b'), C('r')},
{C('#'), C('3'), C('C'), C('S'), C('c'), C('s')},
{C('$'), C('4'), C('D'), C('T'), C('d'), C('t')},
{C('%'), C('5'), C('E'), C('U'), C('e'), C('u')},
{C('&'), C('6'), C('F'), C('V'), C('f'), C('v')},
{C('\''), C('7'), C('G'), C('W'), C('g'), C('w')},
{C('('), C('8'), C('H'), C('X'), C('h'), C('x')},
{C(')'), C('9'), C('I'), C('Y'), C('i'), C('y')},
{C('*'), C(':'), C('J'), C('Z'), C('j'), C('z')},
{C('+'), C(';'), C('K'), C('['), C('k'), C('{')},
{C(','), C('<'), C('L'), C('\\'), C('l'), C('|')},
{C('-'), C('='), C('M'), C(']'), C('m'), C('}')},
{C('.'), C('>'), C('N'), C('^'), C('n'), C('~')},
{C('/'), C('?'), C('O'), C('_'), C('o'), C('\u007F')},
}
// TODO: Correct to Match docs
decNRCGerman = GlyphTable{
{C(' '), C('0'), C('@'), C('P'), C('`'), C('p')},
{C('!'), C('1'), C('A'), C('Q'), C('a'), C('q')},
{C('"'), C('2'), C('B'), C('R'), C('b'), C('r')},
{C('#'), C('3'), C('C'), C('S'), C('c'), C('s')},
{C('$'), C('4'), C('D'), C('T'), C('d'), C('t')},
{C('%'), C('5'), C('E'), C('U'), C('e'), C('u')},
{C('&'), C('6'), C('F'), C('V'), C('f'), C('v')},
{C('\''), C('7'), C('G'), C('W'), C('g'), C('w')},
{C('('), C('8'), C('H'), C('X'), C('h'), C('x')},
{C(')'), C('9'), C('I'), C('Y'), C('i'), C('y')},
{C('*'), C(':'), C('J'), C('Z'), C('j'), C('z')},
{C('+'), C(';'), C('K'), C('['), C('k'), C('{')},
{C(','), C('<'), C('L'), C('\\'), C('l'), C('|')},
{C('-'), C('='), C('M'), C(']'), C('m'), C('}')},
{C('.'), C('>'), C('N'), C('^'), C('n'), C('~')},
{C('/'), C('?'), C('O'), C('_'), C('o'), C('\u007F')},
}
// TODO: Correct to Match docs
decNRCItalian = GlyphTable{
{C(' '), C('0'), C('@'), C('P'), C('`'), C('p')},
{C('!'), C('1'), C('A'), C('Q'), C('a'), C('q')},
{C('"'), C('2'), C('B'), C('R'), C('b'), C('r')},
{C('#'), C('3'), C('C'), C('S'), C('c'), C('s')},
{C('$'), C('4'), C('D'), C('T'), C('d'), C('t')},
{C('%'), C('5'), C('E'), C('U'), C('e'), C('u')},
{C('&'), C('6'), C('F'), C('V'), C('f'), C('v')},
{C('\''), C('7'), C('G'), C('W'), C('g'), C('w')},
{C('('), C('8'), C('H'), C('X'), C('h'), C('x')},
{C(')'), C('9'), C('I'), C('Y'), C('i'), C('y')},
{C('*'), C(':'), C('J'), C('Z'), C('j'), C('z')},
{C('+'), C(';'), C('K'), C('['), C('k'), C('{')},
{C(','), C('<'), C('L'), C('\\'), C('l'), C('|')},
{C('-'), C('='), C('M'), C(']'), C('m'), C('}')},
{C('.'), C('>'), C('N'), C('^'), C('n'), C('~')},
{C('/'), C('?'), C('O'), C('_'), C('o'), C('\u007F')},
}
// TODO: Correct to Match docs
decNRCNorwegianDanish = GlyphTable{
{C(' '), C('0'), C('@'), C('P'), C('`'), C('p')},
{C('!'), C('1'), C('A'), C('Q'), C('a'), C('q')},
{C('"'), C('2'), C('B'), C('R'), C('b'), C('r')},
{C('#'), C('3'), C('C'), C('S'), C('c'), C('s')},
{C('$'), C('4'), C('D'), C('T'), C('d'), C('t')},
{C('%'), C('5'), C('E'), C('U'), C('e'), C('u')},
{C('&'), C('6'), C('F'), C('V'), C('f'), C('v')},
{C('\''), C('7'), C('G'), C('W'), C('g'), C('w')},
{C('('), C('8'), C('H'), C('X'), C('h'), C('x')},
{C(')'), C('9'), C('I'), C('Y'), C('i'), C('y')},
{C('*'), C(':'), C('J'), C('Z'), C('j'), C('z')},
{C('+'), C(';'), C('K'), C('['), C('k'), C('{')},
{C(','), C('<'), C('L'), C('\\'), C('l'), C('|')},
{C('-'), C('='), C('M'), C(']'), C('m'), C('}')},
{C('.'), C('>'), C('N'), C('^'), C('n'), C('~')},
{C('/'), C('?'), C('O'), C('_'), C('o'), C('\u007F')},
}
// TODO: Correct to Match docs
decNRCSpanish = GlyphTable{
{C(' '), C('0'), C('@'), C('P'), C('`'), C('p')},
{C('!'), C('1'), C('A'), C('Q'), C('a'), C('q')},
{C('"'), C('2'), C('B'), C('R'), C('b'), C('r')},
{C('#'), C('3'), C('C'), C('S'), C('c'), C('s')},
{C('$'), C('4'), C('D'), C('T'), C('d'), C('t')},
{C('%'), C('5'), C('E'), C('U'), C('e'), C('u')},
{C('&'), C('6'), C('F'), C('V'), C('f'), C('v')},
{C('\''), C('7'), C('G'), C('W'), C('g'), C('w')},
{C('('), C('8'), C('H'), C('X'), C('h'), C('x')},
{C(')'), C('9'), C('I'), C('Y'), C('i'), C('y')},
{C('*'), C(':'), C('J'), C('Z'), C('j'), C('z')},
{C('+'), C(';'), C('K'), C('['), C('k'), C('{')},
{C(','), C('<'), C('L'), C('\\'), C('l'), C('|')},
{C('-'), C('='), C('M'), C(']'), C('m'), C('}')},
{C('.'), C('>'), C('N'), C('^'), C('n'), C('~')},
{C('/'), C('?'), C('O'), C('_'), C('o'), C('\u007F')},
}
// TODO: Correct to Match docs
decNRCSwedish = GlyphTable{
{C(' '), C('0'), C('@'), C('P'), C('`'), C('p')},
{C('!'), C('1'), C('A'), C('Q'), C('a'), C('q')},
{C('"'), C('2'), C('B'), C('R'), C('b'), C('r')},
{C('#'), C('3'), C('C'), C('S'), C('c'), C('s')},
{C('$'), C('4'), C('D'), C('T'), C('d'), C('t')},
{C('%'), C('5'), C('E'), C('U'), C('e'), C('u')},
{C('&'), C('6'), C('F'), C('V'), C('f'), C('v')},
{C('\''), C('7'), C('G'), C('W'), C('g'), C('w')},
{C('('), C('8'), C('H'), C('X'), C('h'), C('x')},
{C(')'), C('9'), C('I'), C('Y'), C('i'), C('y')},
{C('*'), C(':'), C('J'), C('Z'), C('j'), C('z')},
{C('+'), C(';'), C('K'), C('['), C('k'), C('{')},
{C(','), C('<'), C('L'), C('\\'), C('l'), C('|')},
{C('-'), C('='), C('M'), C(']'), C('m'), C('}')},
{C('.'), C('>'), C('N'), C('^'), C('n'), C('~')},
{C('/'), C('?'), C('O'), C('_'), C('o'), C('\u007F')},
}
// TODO: Correct to Match docs
decNRCSwiss = GlyphTable{
{C(' '), C('0'), C('@'), C('P'), C('`'), C('p')},
{C('!'), C('1'), C('A'), C('Q'), C('a'), C('q')},
{C('"'), C('2'), C('B'), C('R'), C('b'), C('r')},
{C('#'), C('3'), C('C'), C('S'), C('c'), C('s')},
{C('$'), C('4'), C('D'), C('T'), C('d'), C('t')},
{C('%'), C('5'), C('E'), C('U'), C('e'), C('u')},
{C('&'), C('6'), C('F'), C('V'), C('f'), C('v')},
{C('\''), C('7'), C('G'), C('W'), C('g'), C('w')},
{C('('), C('8'), C('H'), C('X'), C('h'), C('x')},
{C(')'), C('9'), C('I'), C('Y'), C('i'), C('y')},
{C('*'), C(':'), C('J'), C('Z'), C('j'), C('z')},
{C('+'), C(';'), C('K'), C('['), C('k'), C('{')},
{C(','), C('<'), C('L'), C('\\'), C('l'), C('|')},
{C('-'), C('='), C('M'), C(']'), C('m'), C('}')},
{C('.'), C('>'), C('N'), C('^'), C('n'), C('~')},
{C('/'), C('?'), C('O'), C('_'), C('o'), C('\u007F')},
}
)
var (
DEC_NRC_British = NewDynamicCodepage(
CharacterSetFromTable(decNRCBritish),
)
DEC_NRC_Dutch = NewDynamicCodepage(
CharacterSetFromTable(decNRCDutch),
)
DEC_NRC_Finnish = NewDynamicCodepage(
CharacterSetFromTable(decNRCFinnish),
)
DEC_NRC_French = NewDynamicCodepage(
CharacterSetFromTable(decNRCFrench),
)
DEC_NRC_FrenchCanadian = NewDynamicCodepage(
CharacterSetFromTable(decNRCFrenchCanadian),
)
DEC_NRC_German = NewDynamicCodepage(
CharacterSetFromTable(decNRCGerman),
)
DEC_NRC_Italian = NewDynamicCodepage(
CharacterSetFromTable(decNRCItalian),
)
DEC_NRC_NorwegianDanish = NewDynamicCodepage(
CharacterSetFromTable(decNRCNorwegianDanish),
)
DEC_NRC_Spanish = NewDynamicCodepage(
CharacterSetFromTable(decNRCSpanish),
)
DEC_NRC_Swedish = NewDynamicCodepage(
CharacterSetFromTable(decNRCSwedish),
)
DEC_NRC_Swiss = NewDynamicCodepage(
CharacterSetFromTable(decNRCSwiss),
)
)

28
encoding/charmap/decSG.go Normal file
View File

@ -0,0 +1,28 @@
package charmap
var (
decSpecialGraphics = GlyphTable{
{N(nil), C('0'), C('@'), C('P'), C('♦'), C('⎻')},
{C('!'), C('1'), C('A'), C('Q'), C('█'), C('─')},
{C('"'), C('2'), C('B'), C('R'), C('␉'), C('⎼')},
{C('#'), C('3'), C('C'), C('S'), C('␌'), C('⎽')},
{C('$'), C('4'), C('D'), C('T'), C('␍'), C('├')},
{C('%'), C('5'), C('E'), C('U'), C('␊'), C('┤')},
{C('&'), C('6'), C('F'), C('V'), C('°'), C('┴')},
{C('\''), C('7'), C('C'), C('W'), C('±'), C('┬')},
{C('('), C('8'), C('H'), C('X'), C('␤'), C('│')},
{C(')'), C('9'), C('I'), C('Y'), C('␋'), C('≤')},
{C('*'), C(':'), C('J'), C('Z'), C('┘'), C('≥')},
{C('+'), C(';'), C('K'), C('['), C('┐'), C('π')},
{C(','), C('<'), C('L'), C('\\'), C('┌'), C('≠')},
{C('-'), C('='), C('M'), C(']'), C('└'), C('£')},
{C('.'), C('>'), C('N'), C('^'), C('┼'), C('·')},
{C('/'), C('?'), C('O'), C('\u2800'), C('⎺'), N(nil)},
}
)
var (
DEC_SpecialGraphics = NewDynamicCodepage(
CharacterSetFromTable(decSpecialGraphics),
)
)

View File

@ -0,0 +1,256 @@
package charmap
import (
"bytes"
"errors"
"io"
"git.sdf.org/CRThaze/vtTools/encoding/codetable"
mnemonic "git.sdf.org/CRThaze/vtTools/encoding/decMSCMnemonics"
equiv "git.sdf.org/CRThaze/vtTools/equivalence"
"golang.org/x/text/encoding"
"golang.org/x/text/transform"
)
var (
ErrUnboundCodepage = errors.New("Codepage is not bound to a codespace")
)
type transformer interface {
transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error)
}
type Codepage struct {
charSet CharacterSet
codeSpace *codetable.GraphicCodeSpace
}
func NewCodepage(set CharacterSet, graphicTable *codetable.GraphicCodeSpace) Codepage {
return Codepage{
charSet: set,
codeSpace: graphicTable,
}
}
type DynamicCodepage struct {
Codepage
}
func NewDynamicCodepage(set CharacterSet) DynamicCodepage {
e := DynamicCodepage{
Codepage{charSet: set},
}
return e
}
func (p *DynamicCodepage) Bind(graphicTable *codetable.GraphicCodeSpace) {
if graphicTable != nil {
p.codeSpace = graphicTable
return
}
p.codeSpace = nil
}
func (p *DynamicCodepage) IsBoundTo(graphicTable *codetable.GraphicCodeSpace) bool {
if p.codeSpace != nil && p.codeSpace == graphicTable {
return true
}
return false
}
func (p *DynamicCodepage) BoundTo() *codetable.GraphicCodeSpace {
return p.codeSpace
}
type ExtendedCodepage struct {
base Codepage
supplemental Codepage
}
func NewExtendedCodepage(sevenBitSet, eighthBitSet CharacterSet) ExtendedCodepage {
return ExtendedCodepage{
base: Codepage{
charSet: sevenBitSet,
codeSpace: &codetable.GLTable,
},
supplemental: Codepage{
charSet: eighthBitSet,
codeSpace: &codetable.GRTable,
},
}
}
func (p Codepage) transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
srcBuf := bytes.NewBuffer(src)
for r, sbn, e := srcBuf.ReadRune(); e != io.EOF; r, sbn, e = srcBuf.ReadRune() {
transBytes := make([]byte, 1, 1)
if e != nil {
err = e
return
} else if r == '\uFFFD' && sbn == 1 {
transBytes[0] = mnemonic.SUB // MSC/ASCII Substituion character.
} else {
transBytes[0] = p.SafeTrans(r)
}
nSrc += sbn
for i, b := range transBytes {
nextIndex := nDst + i
if len(dst) < nextIndex+1 {
err = transform.ErrShortDst
return
}
dst[nDst+i] = b
nDst++
}
}
return
}
func (p ExtendedCodepage) transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
srcBuf := bytes.NewBuffer(src)
for r, sbn, e := srcBuf.ReadRune(); e != io.EOF; r, sbn, e = srcBuf.ReadRune() {
transBytes := make([]byte, 1, 1)
if e != nil {
err = e
return
} else if r == '\uFFFD' && sbn == 1 {
transBytes[0] = mnemonic.SUB // MSC/ASCII Substituion character.
} else {
if tb, e := p.base.Trans(equiv.Normalize(r)); e == nil {
transBytes[0] = tb
} else {
transBytes[0] = p.supplemental.SafeTrans(r)
}
}
nSrc += sbn
for i, b := range transBytes {
nextIndex := nDst + i
if len(dst) < nextIndex+1 {
err = transform.ErrShortDst
return
}
dst[nDst+i] = b
nDst++
}
}
return
}
type vtEncEncoder struct {
transform.NopResetter
charmap transformer
}
func (en vtEncEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
return en.charmap.transform(dst, src, atEOF)
}
type vtEncDecoder struct {
transform.NopResetter
charmap Codepage
}
func (d vtEncDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
// TODO: Implement decode by looking up rune in charmap.
return 0, 0, nil
}
func (p Codepage) NewDecoder() *encoding.Decoder {
return &encoding.Decoder{
Transformer: vtEncDecoder{},
}
}
func (p Codepage) NewEncoder() *encoding.Encoder {
return &encoding.Encoder{
Transformer: vtEncEncoder{},
}
}
func (p ExtendedCodepage) NewDecoder() *encoding.Decoder {
return &encoding.Decoder{
Transformer: vtEncDecoder{},
}
}
func (p ExtendedCodepage) NewEncoder() *encoding.Encoder {
return &encoding.Encoder{
Transformer: vtEncEncoder{},
}
}
func (p Codepage) Trans(r rune) (byte, error) {
if p.codeSpace == nil {
return 0, ErrUnboundCodepage
}
if b, ok := decMSCControlFull[r]; ok {
return b, nil
} else if xy := p.charSet.lookup.Get(r); xy != nil {
// Discarding the error since we are confident
// the coordinate will not be out of range.
b, _ = p.codeSpace.Get(xy[0], xy[1])
return b, nil
}
return 0, errors.New("No translation for rune.")
}
func (p Codepage) MustTrans(r rune) byte {
if b, err := p.Trans(r); err == nil {
return b
}
return mnemonic.SUB
}
func (p Codepage) SafeTrans(r rune) byte {
return p.MustTrans(equiv.Normalize(r))
}
// // TransDynamic first calls Trans. If Trans returns an error, then it attempts to find the rune
// // anywhere within the GraphicRepetoir. If it is able to find the rune, it adds the necessary
// // escape sequences to temporarily load the needed character set to properly display the matching
// // symbol. If none can be found, it retuns the same fall-back code-point as MustTrans.
// func (e vtEncEncoder) TransDynamic(r rune) []byte {
// if s, err := vt.Trans(r); err == nil {
// return s
// }
// if vt.graphicRegistry[2].register.designated {
// if vt.graphicRegistry[2].register.set.lookup.Has(r) {
// if bs, err := vt.SingleShiftRune(2, r); err == nil {
// return bs
// } else {
// panic(err)
// }
// }
// }
// if vt.graphicRegistry[3].register.designated {
// if vt.graphicRegistry[3].register.set.lookup.Has(r) {
// if bs, err := vt.SingleShiftRune(3, r); err == nil {
// return bs
// } else {
// panic(err)
// }
// }
// }
// for i, set := range vt.graphicRepetoire {
// if set == nil || !set.lookup.Has(r) {
// continue
// }
// // TODO: Don't leave this in the register.
// if err := vt.DesignateCharSet(i, 3); err != nil {
// panic(err)
// }
// if bs, err := vt.SingleShiftRune(3, r); err == nil {
// return bs
// } else {
// panic(err)
// }
// }
// return UNKNOWN[:]
// }
// // SafeTrans uses an equivalency table for the given rune to determine a similiar rune that is
// // present in the GraphicRepetoir. It then calls TransDynamic with that rune.
// func (e vtEncEncoder) SafeTransDynamic(r rune) []byte {
// return vt.TransDynamic(requiem.Equivalence.Normalize(r))
// }

View File

@ -0,0 +1,90 @@
package charmap
import (
ct "git.sdf.org/CRThaze/vtTools/encoding/codetable"
)
const (
UnicodeSP = '\u0020'
UnicodeDEL = '\u007F'
TableRowCount = ct.CodeSpaceRowCount
TableColumnCount = ct.GraphicCodeSpaceColumnCount
)
type Character *rune
func C(r rune) Character {
return &r
}
func N(r *rune) Character {
return r
}
type GlyphTable [TableRowCount][TableColumnCount]Character
type lookupTable map[rune][2]int
func (t lookupTable) Has(r rune) bool {
if _, ok := t[r]; ok {
return true
}
return false
}
func (t lookupTable) Get(r rune) *[2]int {
if coords, ok := t[r]; ok {
return &coords
}
return nil
}
type CharacterSet struct {
table GlyphTable
lookup lookupTable
}
func CharacterSetFromTable(t GlyphTable) CharacterSet {
set := CharacterSet{}
set.table = t
// Intialize the lookup with the immutable slots.
set.lookup = lookupTable{
UnicodeSP: [2]int{0, 0},
UnicodeDEL: [2]int{
(ct.CodeSpaceRowCount - 1),
(ct.GraphicCodeSpaceColumnCount - 1),
},
}
for i, row := range set.table {
for j, graph := range row {
// These runes are mapped immutably.
if *graph == UnicodeSP || *graph == UnicodeDEL {
continue
} else if i == 0 && j == 0 {
continue
} else if i == (ct.CodeSpaceRowCount-1) && j == (ct.GraphicCodeSpaceColumnCount-1) {
continue
}
// Skip empty GlyphTable slots.
if graph == nil {
continue
}
set.lookup[*graph] = [2]int{i, j}
}
}
return set
}
func CharacterSetFromArray(a [94]Character) CharacterSet {
set := CharacterSet{}
set.lookup = lookupTable{}
var k int
for i, graph := range a {
k = i / TableRowCount
j := i % TableColumnCount
set.table[j][k] = graph
if graph != nil {
set.lookup[*graph] = [2]int{i, j}
}
}
return set
}

View File

@ -0,0 +1,276 @@
package codetable
const (
// Column 0
C00R00 byte = 0b0000_0000 + iota
C00R01
C00R02
C00R03
C00R04
C00R05
C00R06
C00R07
C00R08
C00R09
C00R10
C00R11
C00R12
C00R13
C00R14
C00R15
// Column 1
C01R00
C01R01
C01R02
C01R03
C01R04
C01R05
C01R06
C01R07
C01R08
C01R09
C01R10
C01R11
C01R12
C01R13
C01R14
C01R15
// Column 2
C02R00
C02R01
C02R02
C02R03
C02R04
C02R05
C02R06
C02R07
C02R08
C02R09
C02R10
C02R11
C02R12
C02R13
C02R14
C02R15
// Column 3
C03R00
C03R01
C03R02
C03R03
C03R04
C03R05
C03R06
C03R07
C03R08
C03R09
C03R10
C03R11
C03R12
C03R13
C03R14
C03R15
// Column 4
C04R00
C04R01
C04R02
C04R03
C04R04
C04R05
C04R06
C04R07
C04R08
C04R09
C04R10
C04R11
C04R12
C04R13
C04R14
C04R15
// Column 5
C05R00
C05R01
C05R02
C05R03
C05R04
C05R05
C05R06
C05R07
C05R08
C05R09
C05R10
C05R11
C05R12
C05R13
C05R14
C05R15
// Column 6
C06R00
C06R01
C06R02
C06R03
C06R04
C06R05
C06R06
C06R07
C06R08
C06R09
C06R10
C06R11
C06R12
C06R13
C06R14
C06R15
// Column 7
C07R00
C07R01
C07R02
C07R03
C07R04
C07R05
C07R06
C07R07
C07R08
C07R09
C07R10
C07R11
C07R12
C07R13
C07R14
C07R15
// Column 8
C08R00
C08R01
C08R02
C08R03
C08R04
C08R05
C08R06
C08R07
C08R08
C08R09
C08R10
C08R11
C08R12
C08R13
C08R14
C08R15
// Column 9
C09R00
C09R01
C09R02
C09R03
C09R04
C09R05
C09R06
C09R07
C09R08
C09R09
C09R10
C09R11
C09R12
C09R13
C09R14
C09R15
// Column 10
C10R00
C10R01
C10R02
C10R03
C10R04
C10R05
C10R06
C10R07
C10R08
C10R09
C10R10
C10R11
C10R12
C10R13
C10R14
C10R15
// Column 11
C11R00
C11R01
C11R02
C11R03
C11R04
C11R05
C11R06
C11R07
C11R08
C11R09
C11R10
C11R11
C11R12
C11R13
C11R14
C11R15
// Column 12
C12R00
C12R01
C12R02
C12R03
C12R04
C12R05
C12R06
C12R07
C12R08
C12R09
C12R10
C12R11
C12R12
C12R13
C12R14
C12R15
// Column 13
C13R00
C13R01
C13R02
C13R03
C13R04
C13R05
C13R06
C13R07
C13R08
C13R09
C13R10
C13R11
C13R12
C13R13
C13R14
C13R15
// Column 14
C14R00
C14R01
C14R02
C14R03
C14R04
C14R05
C14R06
C14R07
C14R08
C14R09
C14R10
C14R11
C14R12
C14R13
C14R14
C14R15
// Column 15
C15R00
C15R01
C15R02
C15R03
C15R04
C15R05
C15R06
C15R07
C15R08
C15R09
C15R10
C15R11
C15R12
C15R13
C15R14
C15R15
)

View File

@ -0,0 +1,50 @@
package codetable
const (
C1 = "C1"
GR = "GR"
)
var C1Table = ControlCodeSpace{
id: C1,
table: controlTable{
controlRow{C08R00, C09R00},
controlRow{C08R01, C09R01},
controlRow{C08R02, C09R02},
controlRow{C08R03, C09R03},
controlRow{C08R04, C09R04},
controlRow{C08R05, C09R05},
controlRow{C08R06, C09R06},
controlRow{C08R07, C09R07},
controlRow{C08R08, C09R08},
controlRow{C08R09, C09R09},
controlRow{C08R10, C09R10},
controlRow{C08R11, C09R11},
controlRow{C08R12, C09R12},
controlRow{C08R13, C09R13},
controlRow{C08R14, C09R14},
controlRow{C08R15, C09R15},
},
}
var GRTable = GraphicCodeSpace{
id: GR,
table: graphicTable{
graphicRow{C10R00, C11R00, C12R00, C13R00, C14R00, C15R00},
graphicRow{C10R01, C11R01, C12R01, C13R01, C14R01, C15R01},
graphicRow{C10R02, C11R02, C12R02, C13R02, C14R02, C15R02},
graphicRow{C10R03, C11R03, C12R03, C13R03, C14R03, C15R03},
graphicRow{C10R04, C11R04, C12R04, C13R04, C14R04, C15R04},
graphicRow{C10R05, C11R05, C12R05, C13R05, C14R05, C15R05},
graphicRow{C10R06, C11R06, C12R06, C13R06, C14R06, C15R06},
graphicRow{C10R07, C11R07, C12R07, C13R07, C14R07, C15R07},
graphicRow{C10R08, C11R08, C12R08, C13R08, C14R08, C15R08},
graphicRow{C10R09, C11R09, C12R09, C13R09, C14R09, C15R09},
graphicRow{C10R10, C11R10, C12R10, C13R10, C14R10, C15R10},
graphicRow{C10R11, C11R11, C12R11, C13R11, C14R11, C15R11},
graphicRow{C10R12, C11R12, C12R12, C13R12, C14R12, C15R12},
graphicRow{C10R13, C11R13, C12R13, C13R13, C14R13, C15R13},
graphicRow{C10R14, C11R14, C12R14, C13R14, C14R14, C15R14},
graphicRow{C10R15, C11R15, C12R15, C13R15, C14R15, C15R15},
},
}

View File

@ -0,0 +1,50 @@
package codetable
const (
C0 = "C0"
GL = "GL"
)
var C0Table = ControlCodeSpace{
id: C0,
table: controlTable{
controlRow{C00R00, C01R00},
controlRow{C00R01, C01R01},
controlRow{C00R02, C01R02},
controlRow{C00R03, C01R03},
controlRow{C00R04, C01R04},
controlRow{C00R05, C01R05},
controlRow{C00R06, C01R06},
controlRow{C00R07, C01R07},
controlRow{C00R08, C01R08},
controlRow{C00R09, C01R09},
controlRow{C00R10, C01R10},
controlRow{C00R11, C01R11},
controlRow{C00R12, C01R12},
controlRow{C00R13, C01R13},
controlRow{C00R14, C01R14},
controlRow{C00R15, C01R15},
},
}
var GLTable = GraphicCodeSpace{
id: GL,
table: graphicTable{
graphicRow{C02R00, C03R00, C04R00, C05R00, C06R00, C07R00},
graphicRow{C02R01, C03R01, C04R01, C05R01, C06R01, C07R01},
graphicRow{C02R02, C03R02, C04R02, C05R02, C06R02, C07R02},
graphicRow{C02R03, C03R03, C04R03, C05R03, C06R03, C07R03},
graphicRow{C02R04, C03R04, C04R04, C05R04, C06R04, C07R04},
graphicRow{C02R05, C03R05, C04R05, C05R05, C06R05, C07R05},
graphicRow{C02R06, C03R06, C04R06, C05R06, C06R06, C07R06},
graphicRow{C02R07, C03R07, C04R07, C05R07, C06R07, C07R07},
graphicRow{C02R08, C03R08, C04R08, C05R08, C06R08, C07R08},
graphicRow{C02R09, C03R09, C04R09, C05R09, C06R09, C07R09},
graphicRow{C02R10, C03R10, C04R10, C05R10, C06R10, C07R10},
graphicRow{C02R11, C03R11, C04R11, C05R11, C06R11, C07R11},
graphicRow{C02R12, C03R12, C04R12, C05R12, C06R12, C07R12},
graphicRow{C02R13, C03R13, C04R13, C05R13, C06R13, C07R13},
graphicRow{C02R14, C03R14, C04R14, C05R14, C06R14, C07R14},
graphicRow{C02R15, C03R15, C04R15, C05R15, C06R15, C07R15},
},
}

View File

@ -0,0 +1,71 @@
package codetable
import "errors"
var (
ErrOutOfBounds = errors.New("Coordinates out of bounds")
)
const (
CodeSpaceRowCount = 16
ControlCodeSpaceColumnCount = 2
GraphicCodeSpaceColumnCount = 6
)
type CodeSpace interface {
Get(int, int) (byte, error)
ID() string
Columns() int
}
type controlRow [ControlCodeSpaceColumnCount]byte
type controlTable [CodeSpaceRowCount]controlRow
type ControlCodeSpace struct {
table controlTable
id string
}
func (t ControlCodeSpace) Get(col, row int) (byte, error) {
colOffset := t.table[0][0] & 0xF0
if col < int(colOffset) || col > int(colOffset+CodeSpaceRowCount) || row < 0 || row > 16 {
return 0, ErrOutOfBounds
}
return t.table[col][row], nil
}
func (t ControlCodeSpace) ID() string {
return t.id
}
func (t ControlCodeSpace) Columns() int {
return len(t.table[0])
}
type graphicRow [GraphicCodeSpaceColumnCount]byte
type graphicTable [CodeSpaceRowCount]graphicRow
type GraphicCodeSpace struct {
table graphicTable
id string
}
func (t GraphicCodeSpace) ColumnOffset() int {
return int(t.table[0][0] & 0xF0) // Get highest order nibble.
}
func (t GraphicCodeSpace) Get(row, col int) (byte, error) {
colOffset := t.ColumnOffset()
if col < colOffset || col > (colOffset+GraphicCodeSpaceColumnCount) || row < 0 || row > 16 {
return 0, ErrOutOfBounds
}
return t.table[col][row], nil
}
func (t GraphicCodeSpace) ID() string {
return t.id
}
func (t GraphicCodeSpace) Columns() int {
return len(t.table[0])
}

24
encoding/decEncodings.go Normal file
View File

@ -0,0 +1,24 @@
package encoding
import (
"git.sdf.org/CRThaze/vtTools/encoding/charmap"
"golang.org/x/text/encoding"
)
var (
DEC_MSC encoding.Encoding = charmap.DEC_MSC
DEC_MSC_ASCII encoding.Encoding = charmap.DEC_MSC_ASCII
DEC_MSC_Supplemental encoding.Encoding = charmap.DEC_MSC_Supplemental
DEC_SpecialGraphics encoding.Encoding = charmap.DEC_SpecialGraphics
DEC_NRC_British encoding.Encoding = charmap.DEC_NRC_British
DEC_NRC_Dutch encoding.Encoding = charmap.DEC_NRC_Dutch
DEC_NRC_Finnish encoding.Encoding = charmap.DEC_NRC_Finnish
DEC_NRC_French encoding.Encoding = charmap.DEC_NRC_French
DEC_NRC_FrenchCanadian encoding.Encoding = charmap.DEC_NRC_FrenchCanadian
DEC_NRC_German encoding.Encoding = charmap.DEC_NRC_German
DEC_NRC_Italian encoding.Encoding = charmap.DEC_NRC_Italian
DEC_NRC_NorwegianDanish encoding.Encoding = charmap.DEC_NRC_NorwegianDanish
DEC_NRC_Spanish encoding.Encoding = charmap.DEC_NRC_Spanish
DEC_NRC_Swedish encoding.Encoding = charmap.DEC_NRC_Swedish
DEC_NRC_Swiss encoding.Encoding = charmap.DEC_NRC_Swiss
)

View File

@ -0,0 +1,77 @@
package control
import (
"git.sdf.org/CRThaze/vtTools/encoding/codetable"
)
// DEC MSC Mnemonics
// C0
const (
NUL = codetable.C00R00
SOH = codetable.C00R01
STX = codetable.C00R02
ETX = codetable.C00R03
EOT = codetable.C00R04
ENQ = codetable.C00R05
ACK = codetable.C00R06
BEL = codetable.C00R07
BS = codetable.C00R08
HT = codetable.C00R09
LF = codetable.C00R10
VT = codetable.C00R11
FF = codetable.C00R12
CR = codetable.C00R13
SO = codetable.C00R14
SI = codetable.C00R15
DLE = codetable.C01R00
DC1 = codetable.C01R01 // XON
DC2 = codetable.C01R02
DC3 = codetable.C01R03 // XOFF
DC4 = codetable.C01R04
NAK = codetable.C01R05
SYN = codetable.C01R06
ETB = codetable.C01R07
CAN = codetable.C01R08
EM = codetable.C01R09
SUB = codetable.C01R10
ESC = codetable.C01R11
FS = codetable.C01R12
GS = codetable.C01R13
RS = codetable.C01R14
US = codetable.C01R15
)
// GL
const (
SP = codetable.C02R00
DEL = codetable.C07R15
)
// C1
const (
IND = codetable.C08R04
NEL = codetable.C08R05
SSA = codetable.C08R06
ESA = codetable.C08R07
HTS = codetable.C08R08
HTJ = codetable.C08R09
VTS = codetable.C08R10
PLD = codetable.C08R11
PLU = codetable.C08R12
RI = codetable.C08R13
SS2 = codetable.C08R14
SS3 = codetable.C08R15
DCS = codetable.C09R00
PU1 = codetable.C09R01
PU2 = codetable.C09R02
STS = codetable.C09R03
CCH = codetable.C09R04
MW = codetable.C09R05
SPA = codetable.C09R06
EPA = codetable.C09R07
CSI = codetable.C09R11
ST = codetable.C09R12
OSC = codetable.C09R13
PM = codetable.C09R14
APC = codetable.C09R15
)

View File

@ -0,0 +1,8 @@
package equivalence
func Normalize(r rune) rune {
if normal, ok := lookupDEC[r]; ok {
return normal
}
return r
}

2170
equivalence/lookup.go Normal file

File diff suppressed because it is too large Load Diff

1
esctrl/control.go Normal file
View File

@ -0,0 +1 @@
package esctrl

2
go.mod
View File

@ -1,3 +1,5 @@
module git.sdf.org/CRThaze/vtTools
go 1.22.5
require golang.org/x/text v0.17.0

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=

View File

@ -1,12 +1,11 @@
package vt220
package main
import (
"os"
"testing"
"time"
)
var term = NewVT220(os.Stdout)
"git.sdf.org/CRThaze/vtTools/term"
)
var testMSCASCIIGraphic = [16][6]rune{
{' ', '0', '@', 'P', '`', 'p'},
@ -65,7 +64,7 @@ var testSpecialGraphics = [16][6]rune{
{'/', '?', 'O', '\u2800', '⎺', 0},
}
func printTest(sl, sr [16][6]rune) {
func printTest(term *term.VT220, sl, sr [16][6]rune) {
for i := 0; i < 16; i++ {
for j := 0; j < 12; j++ {
term.Print(" ")
@ -91,10 +90,11 @@ func printTest(sl, sr [16][6]rune) {
term.Print("\n")
}
func TestOutput(t *testing.T) {
func main() {
term := term.NewVT220(os.Stdout)
term.Init()
printTest(testMSCASCIIGraphic, testMSCSupplementalGraphic)
printTest(term, testMSCASCIIGraphic, testMSCSupplementalGraphic)
term.LockShift(2, true)
printTest(testMSCASCIIGraphic, testSpecialGraphics)
printTest(term, testMSCASCIIGraphic, testSpecialGraphics)
term.LockShift(1, true)
}

View File

@ -1,20 +0,0 @@
package requivalence
type equivalenceTable map[rune]rune
type EquivalenceTable struct {
et equivalenceTable
}
func (table EquivalenceTable) Normalize(r rune) rune {
if normal, ok := table.et[r]; ok {
return normal
}
return r
}
var Equivalence = EquivalenceTable{
et: equivalenceTable{
'a': 'a',
},
}

3
sixel/sixel.go Normal file
View File

@ -0,0 +1,3 @@
package sixel
type SixelGlyph [15]byte

101
term/codetable.go Normal file
View File

@ -0,0 +1,101 @@
// package term
// import (
// "errors"
// "io"
// )
// // type controlRow [2]byte
// // type controlTable [16]controlRow
// type rangeLookup map[rune]*byte
// type ControlRange struct {
// table controlTable
// set rangeLookup
// colOffset uint8
// }
// func (r *ControlRange) Get(col, row int) (byte, error) {
// if col < int(r.colOffset) || col >= int(r.colOffset)+2 || row < 0 || row > 15 {
// return byte(0), errors.New("Coordinates Out Of Bounds")
// }
// return glTable[row][col-int(r.colOffset)], nil
// }
// func (r *ControlRange) Lookup(ru rune) *byte {
// if point, ok := r.set[ru]; ok {
// return point
// }
// return nil
// }
// // type graphicRow [6]byte
// // type graphicTable [16]graphicRow
// type GraphicRange struct {
// table graphicTable
// set rangeLookup
// colOffset uint8
// lockShifts map[GraphicRegister]ControlSequence
// singleShifts map[GraphicRegister]ControlSequence
// }
// func (r *GraphicRange) Get(col, row int) (byte, error) {
// if col < int(r.colOffset) || col >= int(r.colOffset)+6 || row < 0 || row > 15 {
// return byte(0), errors.New("Coordinates Out Of Bounds")
// }
// return r.table[row][col-int(r.colOffset)], nil
// }
// func (r *GraphicRange) Lookup(ru rune) *byte {
// if point, ok := r.set[ru]; ok {
// return point
// }
// return nil
// }
// func (r *GraphicRange) load(gOverlay *CharacterSet) {
// if r.set == nil {
// r.set = rangeLookup{}
// }
// for i, row := range gOverlay.table {
// for j, graph := range row {
// if graph == nil {
// continue
// }
// r.set[*graph] = &r.table[i][j]
// }
// }
// // fmt.Printf("%+v", r.set)
// }
// func (rg *GraphicRange) lockShift(register GraphicRegister, w io.Writer) error {
// if rg.lockShifts == nil {
// return errors.New("Register does not have defined Lock Shifts")
// }
// if _, ok := rg.lockShifts[register]; !ok {
// return errors.New("This register cannot be Lock Shifted into this Range.")
// }
// rg.lockShifts[register].Invoke(w)
// rg.load(register.register.set)
// return nil
// }
// func (rg *GraphicRange) singleShift(register GraphicRegister) ([]byte, error) {
// if rg.singleShifts == nil {
// return nil, errors.New("Register does not have defined Single Shifts")
// }
// if _, ok := rg.singleShifts[register]; !ok {
// return nil, errors.New("This register cannot be Single Shifted into this Range.")
// }
// return rg.singleShifts[register].Bytes(), nil
// }
// func (rg *GraphicRange) Load(register GraphicRegister) error {
// return rg.Load(register)
// }
// // Negative Question Mark.
// // var UNKNOWN = NewControlSequence(CSI, C03R07, C06R13, C03R15, CSI, C03R00, C06R13).Bytes()
// var UNKNOWN = append(append(SGR(SGRPs.Negative).Bytes(), C03R15), SGR(SGRPs.Positive).Bytes()...)

View File

@ -1,7 +1,10 @@
package vt220
package control
import (
"io"
ct "git.sdf.org/CRThaze/vtTools/encoding/codetable"
mnemonic "git.sdf.org/CRThaze/vtTools/encoding/decMSCMnemonics"
)
type ControlSequence struct {
@ -67,34 +70,34 @@ func SGR(attrs ...SGRAttr) ControlSequence {
// extra byte for delimitation.
ba := make([]byte, 0, (2 + (len(attrs) * 3)))
// Append SGR sequence initialization control character.
ba = append(ba, CSI)
ba = append(ba, mnemonic.CSI)
for i, attr := range attrs {
if i > 0 {
// Append Ps delimiter.
ba = append(ba, C03R11)
ba = append(ba, ct.C03R11)
}
if attr.not {
// Append negation byte.
ba = append(ba, C03R02)
ba = append(ba, ct.C03R02)
}
// Append attr ID byte.
ba = append(ba, attr.ps)
}
// Append SGR terminator.
ba = append(ba, C06R13)
ba = append(ba, ct.C06R13)
return NewControlSequence(ba...)
}
var (
// Prefixes
EscapeCtrlPfx = NewControlSequencePrefix(ESC)
DesignateCharSetR0CtrlPfx = EscapeCtrlPfx.Append(C02R08)
DesignateCharSetR1CtrlPfx = EscapeCtrlPfx.Append(C02R09)
DesignateCharSetR2CtrlPfx = EscapeCtrlPfx.Append(C02R10)
DesignateCharSetR3CtrlPfx = EscapeCtrlPfx.Append(C02R11)
EscapeCtrlPfx = NewControlSequencePrefix(mnemonic.ESC)
DesignateCharSetR0CtrlPfx = EscapeCtrlPfx.Append(ct.C02R08)
DesignateCharSetR1CtrlPfx = EscapeCtrlPfx.Append(ct.C02R09)
DesignateCharSetR2CtrlPfx = EscapeCtrlPfx.Append(ct.C02R10)
DesignateCharSetR3CtrlPfx = EscapeCtrlPfx.Append(ct.C02R11)
// Complete Sequences
ShiftCtrl = struct {
Shifts = struct {
LockShiftG0,
LockShiftG1,
LockShiftG1Right,
@ -115,17 +118,17 @@ var (
SS3 ControlSequence
}{
// Lock Shifts
LockShiftG0: NewControlSequence(SI),
LockShiftG1: NewControlSequence(SO),
LockShiftG1Right: EscapeCtrlPfx.With(C07R14),
LockShiftG2: EscapeCtrlPfx.With(C06R14),
LockShiftG2Right: EscapeCtrlPfx.With(C07R13),
LockShiftG3: EscapeCtrlPfx.With(C06R15),
LockShiftG3Right: EscapeCtrlPfx.With(C07R12),
LockShiftG0: NewControlSequence(mnemonic.SI),
LockShiftG1: NewControlSequence(mnemonic.SO),
LockShiftG1Right: EscapeCtrlPfx.With(ct.C07R14),
LockShiftG2: EscapeCtrlPfx.With(ct.C06R14),
LockShiftG2Right: EscapeCtrlPfx.With(ct.C07R13),
LockShiftG3: EscapeCtrlPfx.With(ct.C06R15),
LockShiftG3Right: EscapeCtrlPfx.With(ct.C07R12),
// Single Shifts
SingleShiftG2: NewControlSequence(SS2),
SingleShiftG3: NewControlSequence(SS3),
SingleShiftG2: NewControlSequence(mnemonic.SS2),
SingleShiftG3: NewControlSequence(mnemonic.SS3),
}
SGRPs = struct {
@ -140,47 +143,52 @@ var (
Positive SGRAttr
}{
AllAttrsOff: SGRAttr{
ps: C03R00,
ps: ct.C03R00,
},
Bold: SGRAttr{
ps: C03R01,
ps: ct.C03R01,
},
Underline: SGRAttr{
ps: C03R04,
ps: ct.C03R04,
},
Blink: SGRAttr{
ps: C03R07,
ps: ct.C03R07,
},
Negative: SGRAttr{
ps: C03R07,
ps: ct.C03R07,
},
NoBold: SGRAttr{
ps: C03R01,
ps: ct.C03R01,
not: true,
},
NoUnderline: SGRAttr{
ps: C03R04,
ps: ct.C03R04,
not: true,
},
NoBlink: SGRAttr{
ps: C03R07,
ps: ct.C03R07,
not: true,
},
Positive: SGRAttr{
ps: C03R07,
ps: ct.C03R07,
not: true,
},
}
)
var UNKNOWN = append(
append(SGR(SGRPs.Negative).Bytes(), ct.C03R15),
SGR(SGRPs.Positive).Bytes()...,
)
func init() {
ShiftCtrl.LS0 = ShiftCtrl.LockShiftG0
ShiftCtrl.LS1 = ShiftCtrl.LockShiftG1
ShiftCtrl.LS1R = ShiftCtrl.LockShiftG1Right
ShiftCtrl.LS2 = ShiftCtrl.LockShiftG2
ShiftCtrl.LS2R = ShiftCtrl.LockShiftG2Right
ShiftCtrl.LS3 = ShiftCtrl.LockShiftG3
ShiftCtrl.LS3R = ShiftCtrl.LockShiftG3Right
ShiftCtrl.SS2 = ShiftCtrl.SingleShiftG2
ShiftCtrl.SS3 = ShiftCtrl.SingleShiftG3
Shifts.LS0 = Shifts.LockShiftG0
Shifts.LS1 = Shifts.LockShiftG1
Shifts.LS1R = Shifts.LockShiftG1Right
Shifts.LS2 = Shifts.LockShiftG2
Shifts.LS2R = Shifts.LockShiftG2Right
Shifts.LS3 = Shifts.LockShiftG3
Shifts.LS3R = Shifts.LockShiftG3Right
Shifts.SS2 = Shifts.SingleShiftG2
Shifts.SS3 = Shifts.SingleShiftG3
}

168
term/graphicRepetoire.go Normal file
View File

@ -0,0 +1,168 @@
package term
import (
"errors"
"git.sdf.org/CRThaze/vtTools/encoding/charmap"
ct "git.sdf.org/CRThaze/vtTools/encoding/codetable"
mnemonic "git.sdf.org/CRThaze/vtTools/encoding/decMSCMnemonics"
"git.sdf.org/CRThaze/vtTools/sixel"
ctrl "git.sdf.org/CRThaze/vtTools/term/control"
)
const (
vt220GraphicRepetoireSize = 15
vt220FontBufferIndex = (vt220GraphicRepetoireSize - 1)
)
type GraphicSet struct {
codepage charmap.DynamicCodepage
dscsInter0 *byte
dscsInter1 *byte
dscsFinal byte
font *[94]sixel.SixelGlyph
}
func (set GraphicSet) IsDownLineLoadable() bool {
if set.font == nil {
return false
}
return true
}
// Dscs returns the DEC-Select-Character-Set byte sequence for this CharacterSet.
func (set GraphicSet) Dscs() []byte {
dscsLen := 1
if set.dscsInter0 != nil {
dscsLen++
}
if set.dscsInter1 != nil {
dscsLen++
}
dscs := make([]byte, dscsLen, dscsLen)
if dscsLen == 2 {
dscs[0] = *set.dscsInter0
} else if dscsLen == 3 {
dscs[1] = *set.dscsInter1
}
dscs[dscsLen-1] = set.dscsFinal
return dscs
}
func (set GraphicSet) DECDLD() (ctrl.ControlSequence, error) {
if !set.IsDownLineLoadable() {
return ctrl.ControlSequence{}, errors.New("Not a Soft Character Set")
}
return ctrl.NewControlSequence(
append(
[]byte{
mnemonic.DCS,
ct.C03R00 + 1, // First (and only) Font Buffer
ct.C03R11, // ;
ct.C03R00 + 1, // Start at first character
ct.C03R11, // ;
ct.C03R00 + 0, // Erase all characters
ct.C03R11, // ;
ct.C03R00 + 0, // 7 column character
ct.C03R11, // ;
ct.C03R00 + 0, // 80 Column width
ct.C03R11, // ;
ct.C03R00 + 0, // Text Cell
ct.C07R11, // {
},
append(
set.Dscs(),
func() []byte {
sixels := make([]byte, 0, (len(set.font)*15)+1)
for i, sxl := range set.font {
sixels = append(sixels, sxl[:]...)
if i == len(set.font)-1 {
sixels = append(sixels, mnemonic.ST) // DECDLD Terminator
} else {
sixels = append(sixels, ct.C03R11) // ;
}
}
return sixels
}()...,
)...,
)...,
), nil
}
type graphicRegister struct {
dcs ctrl.ControlSequencePrefix
set *GraphicSet
lockShift ctrl.ControlSequence
lockShiftRight *ctrl.ControlSequence
singleShift *ctrl.ControlSequence
// designated bool
}
type GraphicRegister struct {
register *graphicRegister
}
func (r GraphicRegister) LockShift() error {
r.register.lockShift.Invoke()
r.register.set.codepage.Bind(ct.GLTable)
}
var vt220GraphicRepetoire = [vt220GraphicRepetoireSize]*GraphicSet{
&GraphicSet{
codepage: charmap.DEC_MSC_ASCII,
dscsFinal: ct.C04R02,
},
&GraphicSet{
codepage: charmap.DEC_MSC_Supplemental,
dscsFinal: ct.C03R12,
},
&GraphicSet{
codepage: charmap.DEC_SpecialGraphics,
dscsFinal: ct.C03R00,
},
&GraphicSet{
codepage: charmap.DEC_NRC_British,
dscsFinal: ct.C04R01,
},
&GraphicSet{
codepage: charmap.DEC_NRC_Dutch,
dscsFinal: ct.C03R04,
},
&GraphicSet{
codepage: charmap.DEC_NRC_Finnish,
dscsFinal: ct.C04R03,
},
&GraphicSet{
codepage: charmap.DEC_NRC_French,
dscsFinal: ct.C05R02,
},
&GraphicSet{
codepage: charmap.DEC_NRC_FrenchCanadian,
dscsFinal: ct.C05R01,
},
&GraphicSet{
codepage: charmap.DEC_NRC_German,
dscsFinal: ct.C04R11,
},
&GraphicSet{
codepage: charmap.DEC_NRC_Italian,
dscsFinal: ct.C05R09,
},
&GraphicSet{
codepage: charmap.DEC_NRC_NorwegianDanish,
dscsFinal: ct.C04R05,
},
&GraphicSet{
codepage: charmap.DEC_NRC_Spanish,
dscsFinal: ct.C05R10,
},
&GraphicSet{
codepage: charmap.DEC_NRC_Swedish,
dscsFinal: ct.C04R08,
},
&GraphicSet{
codepage: charmap.DEC_NRC_Swiss,
dscsFinal: ct.C03R13,
},
nil,
}

365
term/vt220.go Normal file
View File

@ -0,0 +1,365 @@
package term
import (
"bytes"
"errors"
"fmt"
"io"
"git.sdf.org/CRThaze/vtTools/encoding/charmap"
ct "git.sdf.org/CRThaze/vtTools/encoding/codetable"
mnemonic "git.sdf.org/CRThaze/vtTools/encoding/decMSCMnemonics"
equiv "git.sdf.org/CRThaze/vtTools/equivalence"
"git.sdf.org/CRThaze/vtTools/sixel"
ctrl "git.sdf.org/CRThaze/vtTools/term/control"
)
type VT220 struct {
writer io.Writer
gl *charmap.DynamicCodepage
gr *charmap.DynamicCodepage
graphicRegistry [4]GraphicRegister
graphicRepetoire [vt220GraphicRepetoireSize]*GraphicSet
fontBuffer **GraphicSet
}
func NewVT220(w io.Writer) *VT220 {
vt := VT220{
writer: w,
gl: &vt220GraphicRepetoire[0].codepage,
gr: &vt220GraphicRepetoire[1].codepage,
graphicRepetoire: vt220GraphicRepetoire,
graphicRegistry: [4]GraphicRegister{
{&graphicRegister{
dcs: ctrl.DesignateCharSetR0CtrlPfx,
lockShift: ctrl.Shifts.LockShiftG0,
}},
{&graphicRegister{
dcs: ctrl.DesignateCharSetR1CtrlPfx,
lockShift: ctrl.Shifts.LockShiftG1,
lockShiftRight: &ctrl.Shifts.LockShiftG1Right,
}},
{&graphicRegister{
dcs: ctrl.DesignateCharSetR2CtrlPfx,
lockShift: ctrl.Shifts.LockShiftG2,
lockShiftRight: &ctrl.Shifts.LockShiftG2Right,
singleShift: &ctrl.Shifts.SingleShiftG2,
}},
{&graphicRegister{
dcs: ctrl.DesignateCharSetR3CtrlPfx,
lockShift: ctrl.Shifts.LockShiftG3,
lockShiftRight: &ctrl.Shifts.LockShiftG3Right,
singleShift: &ctrl.Shifts.SingleShiftG3,
}},
},
// c0: ControlRange{
// table: c0Table,
// colOffset: 0,
// set: rangeLookup{
// '\a': &c0Table[7][0],
// '\b': &c0Table[8][0],
// '\t': &c0Table[9][0],
// '\n': &c0Table[10][0],
// '\v': &c0Table[11][0],
// '\f': &c0Table[12][0],
// '\r': &c0Table[13][0],
// },
// },
// gl: GraphicRange{
// table: glTable,
// colOffset: 2,
// },
// c1: ControlRange{
// table: c1Table,
// colOffset: 8,
// },
// gr: GraphicRange{
// table: grTable,
// colOffset: 10,
// },
}
vt.fontBuffer = &vt.graphicRepetoire[vt220FontBufferIndex]
// vt.gl.lockShifts = map[GraphicRegister]ctrl.ControlSequence{
// vt.graphicRegistry[0]: ctrl.Shifts.LockShiftG0,
// vt.graphicRegistry[1]: ctrl.Shifts.LockShiftG1,
// vt.graphicRegistry[2]: ctrl.Shifts.LockShiftG2,
// vt.graphicRegistry[3]: ctrl.Shifts.LockShiftG3,
// }
// vt.gl.singleShifts = map[GraphicRegister]ctrl.ControlSequence{
// vt.graphicRegistry[2]: ctrl.Shifts.SingleShiftG2,
// vt.graphicRegistry[3]: ctrl.Shifts.SingleShiftG3,
// }
// vt.gr.lockShifts = map[GraphicRegister]ctrl.ControlSequence{
// vt.graphicRegistry[1]: ctrl.Shifts.LockShiftG1Right,
// vt.graphicRegistry[2]: ctrl.Shifts.LockShiftG2Right,
// vt.graphicRegistry[3]: ctrl.Shifts.LockShiftG3Right,
// }
// vt.DesignateCharSet(0, 0)
// vt.DesignateCharSet(1, 1)
// vt.DesignateCharSet(2, 2)
return &vt
}
func (vt *VT220) DesignateCharSet(grIndex, register int) error {
if grIndex < 0 || grIndex > 14 {
return errors.New("Invalid Graphic Repetoire Index")
} else if vt.graphicRepetoire[grIndex] == nil {
return errors.New("Font Buffer empty.")
} else if register < 0 || register > 3 {
return errors.New("Invalid Graphic Register")
}
// Invoke designation control sequence.
vt.graphicRegistry[register].register.dcs.With(
vt.graphicRepetoire[grIndex].Dscs()...,
).Invoke(vt.writer)
vt.graphicRegistry[register].register.set = vt.graphicRepetoire[grIndex]
return nil
}
func (vt *VT220) InvokeCtrlSequence(cs ctrl.ControlSequence) (n int, err error) {
return vt.writer.Write(cs.Bytes())
}
func (vt *VT220) LockShift(registerIndex int, right bool) error {
if registerIndex < 0 || registerIndex > 3 {
return errors.New("Invalid Graphic Register")
}
register := vt.graphicRegistry[registerIndex]
shift := register.register.lockShift
table := ct.GLTable
glr := &vt.gl
if right {
if register.register.lockShiftRight == nil {
return errors.New("Lock Shift Right not supported for this register.")
}
shift = *register.register.lockShiftRight
table = ct.GRTable
glr = &vt.gr
}
shift.Invoke(vt.writer)
register.register.set.codepage.Bind(&table)
*glr = &register.register.set.codepage
return nil
}
func (vt *VT220) SingleShiftRune(registerIndex int, r rune) ([]byte, error) {
if registerIndex < 2 || registerIndex > 3 {
return nil, errors.New("Invalid Graphic Register")
}
shift := vt.graphicRegistry[registerIndex].register.singleShift
if shift != nil {
return nil, errors.New("Register cannot be Single Shifted")
}
shiftBytes := shift.Bytes()
bs := make([]byte, 0, len(shiftBytes)+1)
bs = append(bs, shiftBytes...)
// Save current binding (probably nil)
current := vt.graphicRegistry[registerIndex].register.set.codepage.BoundTo()
// Bind to GL
vt.graphicRegistry[registerIndex].register.set.codepage.Bind(&ct.GLTable)
// Translate rune
b, err := vt.graphicRegistry[registerIndex].register.set.codepage.Trans(r)
// Revert binding
vt.graphicRegistry[registerIndex].register.set.codepage.Bind(current)
if err != nil {
return nil, errors.New("Rune does not map to the character set in the given register.")
}
bs = append(bs, b)
return bs, nil
}
func (vt *VT220) LoadDownLineCharacterSet(runeMap charmap.GlyphTable, font *[94]sixel.SixelGlyph) error {
dscsInter0 := mnemonic.SP
*vt.fontBuffer = &GraphicSet{
codepage: charmap.NewDynamicCodepage(
charmap.CharacterSetFromTable(runeMap),
),
dscsInter0: &dscsInter0, // SP
dscsFinal: ct.C04R00, // @
font: font,
}
decdld, err := (*vt.fontBuffer).DECDLD()
if err != nil {
return err
}
decdld.Invoke(vt.writer)
return nil
}
func (vt VT220) TermID() string {
return "vt220"
}
// // Trans takes a rune and finds the matching code point in either the GL or the GR graphics table.
// // The code-point is then returned as a string.
// // If no matching code-point could be found, then an error is returned.
// func (vt *VT220) Trans(r rune) ([]byte, error) {
// vt.wasInit()
// ba := [1]byte{}
// if b := vt.c0.Lookup(r); b != nil {
// ba[0] = *b
// } else if b := vt.gl.Lookup(r); b != nil {
// ba[0] = *b
// } else if b := vt.gr.Lookup(r); b != nil {
// ba[0] = *b
// } else {
// return nil, errors.New("No translation for rune.")
// }
// return ba[:], nil
// }
// // MustTrans takes a rune and calls the Trans function. if the Trans function returns an error then
// // MustTrans returns the code-point for a question mark on the default graphics set (0x3F).
// func (vt *VT220) MustTrans(r rune) []byte {
// if bs, err := vt.Trans(r); err == nil {
// return bs
// }
// bs := UNKNOWN
// return bs[:]
// }
// // SafeTrans uses an equivalency table to normalize the given rune if it is visually similar
// // enough to a unicode character mapped in the Graphic Repetoire.
// // It then calls MustTrans with the normalized rune.
// func (vt *VT220) SafeTrans(r rune) []byte {
// return vt.MustTrans(equiv.Normalize(r))
// }
// // TransDynamic first calls Trans. If Trans returns an error, then it attempts to find the rune
// // anywhere within the GraphicRepetoir. If it is able to find the rune, it adds the necessary
// // escape sequences to temporarily load the needed character set to properly display the matching
// // symbol. If none can be found, it retuns the same fall-back code-point as MustTrans.
// func (vt *VT220) TransDynamic(r rune) []byte {
// if s, err := vt.Trans(r); err == nil {
// return s
// }
// if vt.graphicRegistry[2].register.designated {
// if vt.graphicRegistry[2].register.set.lookup.Has(r) {
// if bs, err := vt.SingleShiftRune(2, r); err == nil {
// return bs
// } else {
// panic(err)
// }
// }
// }
// if vt.graphicRegistry[3].register.designated {
// if vt.graphicRegistry[3].register.set.lookup.Has(r) {
// if bs, err := vt.SingleShiftRune(3, r); err == nil {
// return bs
// } else {
// panic(err)
// }
// }
// }
// for i, set := range vt.graphicRepetoire {
// if set == nil || !set.lookup.Has(r) {
// continue
// }
// // TODO: Don't leave this in the register.
// if err := vt.DesignateCharSet(i, 3); err != nil {
// panic(err)
// }
// if bs, err := vt.SingleShiftRune(3, r); err == nil {
// return bs
// } else {
// panic(err)
// }
// }
// return UNKNOWN[:]
// }
// // SafeTrans uses an equivalency table for the given rune to determine a similiar rune that is
// // present in the GraphicRepetoir. It then calls TransDynamic with that rune.
// func (vt *VT220) SafeTransDynamic(r rune) []byte {
// return vt.TransDynamic(equiv.Normalize(r))
// }
func (vt VT220) write(buf *bytes.Buffer) (n int, err error) {
writeErr := func(p []byte) bool {
var bn int
bn, err = vt.writer.Write(p)
n += bn
if err != nil {
return true
}
return false
}
for r, sbn, e := buf.ReadRune(); e != io.EOF; r, sbn, e = buf.ReadRune() {
if e != nil {
err = e
return
} else if r == '\uFFFD' && sbn == 1 {
if writeErr(ctrl.UNKNOWN) {
return
}
continue
}
if tb, e := vt.gl.Trans(equiv.Normalize(r)); e == nil {
if writeErr([]byte{tb}) {
return
}
continue
} else if tb, e := vt.gr.Trans(equiv.Normalize(r)); e == nil {
if writeErr([]byte{tb}) {
return
}
continue
} else if tb, e := vt.SingleShiftRune(2, r); e == nil {
if writeErr(tb) {
return
}
continue
} else if tb, e := vt.SingleShiftRune(3, r); e == nil {
if writeErr(tb) {
return
}
continue
}
bytesSoFar := n
for i, set := range vt.graphicRepetoire {
saveBinding := set.codepage.BoundTo()
set.codepage.Bind(&ct.GLTable)
var byteAccumulator []byte
if tb, e := set.codepage.Trans(r); e == nil {
savedSet := vt.graphicRegistry[3].register.set
byteAccumulator = vt.graphicRegistry[3].register.dcs.With(
vt.graphicRepetoire[i].Dscs()...,
).Bytes()
shift := vt.graphicRegistry[3].register.singleShift
byteAccumulator = append(byteAccumulator, shift.Bytes()...)
byteAccumulator = append(byteAccumulator, tb)
byteAccumulator = vt.graphicRegistry[3].register.dcs.With(
savedSet.Dscs()...,
).Bytes()
}
set.codepage.Bind(saveBinding)
if writeErr(byteAccumulator) {
return
}
if n > bytesSoFar {
break
}
}
}
return
}
func (t VT220) Write(p []byte) (n int, err error) {
return t.write(bytes.NewBuffer(p))
}
func (t VT220) Print(a ...any) (n int, err error) {
return t.write(bytes.NewBufferString(fmt.Sprint(a...)))
}
func (t VT220) Printf(format string, a ...any) (n int, err error) {
return t.Print(fmt.Sprintf(format, a...))
}
func (t VT220) Println(a ...any) (n int, err error) {
return t.Print(fmt.Sprintln(a...))
}

155
term/vt220_test.go Normal file
View File

@ -0,0 +1,155 @@
package term
import (
"bytes"
"io"
// "os"
"testing"
// "time"
)
var expected = []byte{
27, 0, 40, 0, 66, // Init
27, 0, 41, 0, 60, // Init
27, 0, 42, 0, 48, // Init
32, 32, 32, 48, 32, 64, 32, 80, 32, 96, 32, 112, 32, 32, 32, 32, 32, 176, 32, 192, 32, 32, 32, 224, 32, 32, 10,
32, 33, 32, 49, 32, 65, 32, 81, 32, 97, 32, 113, 32, 32, 32, 161, 32, 177, 32, 193, 32, 209, 32, 225, 32, 241, 10,
32, 34, 32, 50, 32, 66, 32, 82, 32, 98, 32, 114, 32, 32, 32, 162, 32, 178, 32, 194, 32, 210, 32, 226, 32, 242, 10,
32, 35, 32, 51, 32, 67, 32, 83, 32, 99, 32, 115, 32, 32, 32, 163, 32, 179, 32, 195, 32, 211, 32, 227, 32, 243, 10,
32, 36, 32, 52, 32, 68, 32, 84, 32, 100, 32, 116, 32, 32, 32, 32, 32, 32, 32, 196, 32, 212, 32, 228, 32, 244, 10,
32, 37, 32, 53, 32, 69, 32, 85, 32, 101, 32, 117, 32, 32, 32, 165, 32, 181, 32, 197, 32, 213, 32, 229, 32, 245, 10,
32, 38, 32, 54, 32, 70, 32, 86, 32, 102, 32, 118, 32, 32, 32, 32, 32, 182, 32, 198, 32, 214, 32, 230, 32, 246, 10,
32, 39, 32, 55, 32, 71, 32, 87, 32, 103, 32, 119, 32, 32, 32, 167, 32, 183, 32, 199, 32, 215, 32, 231, 32, 247, 10,
32, 40, 32, 56, 32, 72, 32, 88, 32, 104, 32, 120, 32, 32, 32, 168, 32, 32, 32, 200, 32, 216, 32, 232, 32, 248, 10,
32, 41, 32, 57, 32, 73, 32, 89, 32, 105, 32, 121, 32, 32, 32, 169, 32, 185, 32, 201, 32, 217, 32, 233, 32, 249, 10,
32, 42, 32, 58, 32, 74, 32, 90, 32, 106, 32, 122, 32, 32, 32, 170, 32, 186, 32, 202, 32, 218, 32, 234, 32, 250, 10,
32, 43, 32, 59, 32, 75, 32, 91, 32, 107, 32, 123, 32, 32, 32, 171, 32, 187, 32, 203, 32, 219, 32, 235, 32, 251, 10,
32, 44, 32, 60, 32, 76, 32, 92, 32, 108, 32, 124, 32, 32, 32, 32, 32, 188, 32, 204, 32, 220, 32, 236, 32, 252, 10,
32, 45, 32, 61, 32, 77, 32, 93, 32, 109, 32, 125, 32, 32, 32, 32, 32, 189, 32, 205, 32, 221, 32, 237, 32, 253, 10,
32, 46, 32, 62, 32, 78, 32, 94, 32, 110, 32, 126, 32, 32, 32, 32, 32, 32, 32, 206, 32, 32, 32, 238, 32, 32, 10,
32, 47, 32, 63, 32, 79, 32, 95, 32, 111, 32, 32, 32, 32, 32, 32, 32, 191, 32, 207, 32, 223, 32, 239, 32, 32, 10,
10, // \n
27, 0, 125, // LockShift
32, 32, 32, 48, 32, 64, 32, 80, 32, 96, 32, 112, 32, 32, 32, 32, 32, 48, 32, 64, 32, 80, 32, 224, 32, 240, 10,
32, 33, 32, 49, 32, 65, 32, 81, 32, 97, 32, 113, 32, 32, 32, 33, 32, 49, 32, 65, 32, 81, 32, 225, 32, 241, 10,
32, 34, 32, 50, 32, 66, 32, 82, 32, 98, 32, 114, 32, 32, 32, 34, 32, 50, 32, 66, 32, 82, 32, 226, 32, 242, 10,
32, 35, 32, 51, 32, 67, 32, 83, 32, 99, 32, 115, 32, 32, 32, 35, 32, 51, 32, 67, 32, 83, 32, 227, 32, 243, 10,
32, 36, 32, 52, 32, 68, 32, 84, 32, 100, 32, 116, 32, 32, 32, 36, 32, 52, 32, 68, 32, 84, 32, 228, 32, 244, 10,
32, 37, 32, 53, 32, 69, 32, 85, 32, 101, 32, 117, 32, 32, 32, 37, 32, 53, 32, 69, 32, 85, 32, 229, 32, 245, 10,
32, 38, 32, 54, 32, 70, 32, 86, 32, 102, 32, 118, 32, 32, 32, 38, 32, 54, 32, 70, 32, 86, 32, 230, 32, 246, 10,
32, 39, 32, 55, 32, 71, 32, 87, 32, 103, 32, 119, 32, 32, 32, 39, 32, 55, 32, 71, 32, 87, 32, 231, 32, 247, 10,
32, 40, 32, 56, 32, 72, 32, 88, 32, 104, 32, 120, 32, 32, 32, 40, 32, 56, 32, 72, 32, 88, 32, 232, 32, 248, 10,
32, 41, 32, 57, 32, 73, 32, 89, 32, 105, 32, 121, 32, 32, 32, 41, 32, 57, 32, 73, 32, 89, 32, 233, 32, 249, 10,
32, 42, 32, 58, 32, 74, 32, 90, 32, 106, 32, 122, 32, 32, 32, 42, 32, 58, 32, 74, 32, 90, 32, 234, 32, 250, 10,
32, 43, 32, 59, 32, 75, 32, 91, 32, 107, 32, 123, 32, 32, 32, 43, 32, 59, 32, 75, 32, 91, 32, 235, 32, 251, 10,
32, 44, 32, 60, 32, 76, 32, 92, 32, 108, 32, 124, 32, 32, 32, 44, 32, 60, 32, 76, 32, 92, 32, 236, 32, 252, 10,
32, 45, 32, 61, 32, 77, 32, 93, 32, 109, 32, 125, 32, 32, 32, 45, 32, 61, 32, 77, 32, 93, 32, 237, 32, 253, 10,
32, 46, 32, 62, 32, 78, 32, 94, 32, 110, 32, 126, 32, 32, 32, 46, 32, 62, 32, 78, 32, 94, 32, 238, 32, 254, 10,
32, 47, 32, 63, 32, 79, 32, 95, 32, 111, 32, 32, 32, 32, 32, 47, 32, 63, 32, 79, 32, 223, 32, 239, 32, 32, 10,
10, // \n
27, 0, 126, // LockShift
}
var testMSCASCIIGraphic = [16][6]rune{
{' ', '0', '@', 'P', '`', 'p'},
{'!', '1', 'A', 'Q', 'a', 'q'},
{'"', '2', 'B', 'R', 'b', 'r'},
{'#', '3', 'C', 'S', 'c', 's'},
{'$', '4', 'D', 'T', 'd', 't'},
{'%', '5', 'E', 'U', 'e', 'u'},
{'&', '6', 'F', 'V', 'f', 'v'},
{'\'', '7', 'G', 'W', 'g', 'w'},
{'(', '8', 'H', 'X', 'h', 'x'},
{')', '9', 'I', 'Y', 'i', 'y'},
{'*', ':', 'J', 'Z', 'j', 'z'},
{'+', ';', 'K', '[', 'k', '{'},
{',', '<', 'L', '\\', 'l', '|'},
{'-', '=', 'M', ']', 'm', '}'},
{'.', '>', 'N', '^', 'n', '~'},
{'/', '?', 'O', '_', 'o', '\u007F'},
}
var testMSCSupplementalGraphic = [16][6]rune{
{0, '°', 'À', 0, 'à', 0},
{'¡', '±', 'Á', 'Ñ', 'á', 'ñ'},
{'¢', '²', 'Â', 'Ò', 'â', 'ò'},
{'£', '³', 'Ã', 'Ó', 'ã', 'ó'},
{0, 0, 'Ä', 'Ô', 'ä', 'ô'},
{'¥', 'µ', 'Å', 'Õ', 'å', 'õ'},
{0, '¶', 'Æ', 'Ö', 'æ', 'ö'},
{'§', '·', 'Ç', 'Œ', 'ç', 'œ'},
{'¤', 0, 'È', 'Ø', 'è', 'ø'},
{'©', '¹', 'É', 'Ù', 'é', 'ù'},
{'ª', 'º', 'Ê', 'Ú', 'ê', 'ú'},
{'«', '»', 'Ë', 'Û', 'ë', 'û'},
{0, '¼', 'Ì', 'Ü', 'ì', 'ü'},
{0, '½', 'Í', 'Ÿ', 'í', 'ÿ'},
{0, 0, 'Î', 0, 'î', 0},
{0, '¿', 'Ï', 'ß', 'ï', 0},
}
var testSpecialGraphics = [16][6]rune{
{0, '0', '@', 'P', '♦', '⎻'},
{'!', '1', 'A', 'Q', '█', '―'},
{'"', '2', 'B', 'R', '␉', '⎼'},
{'#', '3', 'C', 'S', '␌', '⎽'},
{'$', '4', 'D', 'T', '␍', '├'},
{'%', '5', 'E', 'U', '␊', '┤'},
{'&', '6', 'F', 'V', '°', '┴'},
{'\'', '7', 'G', 'W', '±', '┬'},
{'(', '8', 'H', 'X', '␤', '│'},
{')', '9', 'I', 'Y', '␋', '≤'},
{'*', ':', 'J', 'Z', '┘', '≥'},
{'+', ';', 'K', '[', '┐', 'π'},
{',', '<', 'L', '\\', '┌', '≠'},
{'-', '=', 'M', ']', '└', '£'},
{'.', '>', 'N', '^', '┼', '·'},
{'/', '?', 'O', '\u2800', '⎺', 0},
}
func printTest(term *VT220, sl, sr [16][6]rune) {
for i := 0; i < 16; i++ {
for j := 0; j < 12; j++ {
term.Print(" ")
if j == 6 {
term.Print(" ")
}
var r rune
if j < 6 {
r = sl[i][j]
} else {
r = sr[i][j-6]
}
if r == 0 || r == '\u007F' {
term.Print(" ")
continue
}
// fmt.Print(string(MustTransRune(r)[:]))
term.Print(string(r))
}
term.Print("\n")
// time.Sleep(time.Millisecond * 250)
}
term.Print("\n")
}
func TestOutput(t *testing.T) {
buf := bytes.Buffer{}
term := NewVT220(&buf)
term.Init()
printTest(term, testMSCASCIIGraphic, testMSCSupplementalGraphic)
term.LockShift(2, true)
printTest(term, testMSCASCIIGraphic, testSpecialGraphics)
term.LockShift(1, true)
if len(expected) != buf.Len() {
t.FailNow()
}
ebi := 0
for b, err := buf.ReadByte(); err != io.EOF; b, err = buf.ReadByte() {
if b != expected[ebi] {
t.FailNow()
}
ebi++
}
}

View File

@ -1,9 +1,7 @@
package vttools
package term
import (
"io"
"git.sdf.org/CRThaze/vtTools/vt220"
)
type TermType struct {
@ -35,7 +33,7 @@ func NewVTTerm(ttype TermType, w io.Writer) VTTerm {
var term VTTerm
switch ttype {
case VT220Type:
term = vt220.NewVT220(w)
term = NewVT220(w)
}
return term
}

View File

@ -1,515 +0,0 @@
package vt220
import (
"errors"
"io"
)
type controlRow [2]byte
type controlTable [16]controlRow
type rangeLookup map[rune]*byte
type ControlRange struct {
table controlTable
set rangeLookup
colOffset uint8
}
func (r *ControlRange) Get(col, row int) (byte, error) {
if col < int(r.colOffset) || col >= int(r.colOffset)+2 || row < 0 || row > 15 {
return byte(0), errors.New("Coordinates Out Of Bounds")
}
return glTable[row][col-int(r.colOffset)], nil
}
func (r *ControlRange) Lookup(ru rune) *byte {
if point, ok := r.set[ru]; ok {
return point
}
return nil
}
type graphicRow [6]byte
type graphicTable [16]graphicRow
type GraphicRange struct {
table graphicTable
set rangeLookup
colOffset uint8
lockShifts map[GraphicRegister]ControlSequence
singleShifts map[GraphicRegister]ControlSequence
}
func (r *GraphicRange) Get(col, row int) (byte, error) {
if col < int(r.colOffset) || col >= int(r.colOffset)+6 || row < 0 || row > 15 {
return byte(0), errors.New("Coordinates Out Of Bounds")
}
return r.table[row][col-int(r.colOffset)], nil
}
func (r *GraphicRange) Lookup(ru rune) *byte {
if point, ok := r.set[ru]; ok {
return point
}
return nil
}
func (r *GraphicRange) load(gOverlay *CharacterSet) {
if r.set == nil {
r.set = rangeLookup{}
}
for i, row := range gOverlay.table {
for j, graph := range row {
if graph == nil {
continue
}
r.set[*graph] = &r.table[i][j]
}
}
// fmt.Printf("%+v", r.set)
}
func (rg *GraphicRange) lockShift(register GraphicRegister, w io.Writer) error {
if rg.lockShifts == nil {
return errors.New("Register does not have defined Lock Shifts")
}
if _, ok := rg.lockShifts[register]; !ok {
return errors.New("This register cannot be Lock Shifted into this Range.")
}
rg.lockShifts[register].Invoke(w)
rg.load(register.register.set)
return nil
}
func (rg *GraphicRange) singleShift(register GraphicRegister) ([]byte, error) {
if rg.singleShifts == nil {
return nil, errors.New("Register does not have defined Single Shifts")
}
if _, ok := rg.singleShifts[register]; !ok {
return nil, errors.New("This register cannot be Single Shifted into this Range.")
}
return rg.singleShifts[register].Bytes(), nil
}
func (rg *GraphicRange) Load(register GraphicRegister) error {
return rg.Load(register)
}
const (
// Column 0
C00R00 byte = 0b0000_0000 + iota
C00R01
C00R02
C00R03
C00R04
C00R05
C00R06
C00R07
C00R08
C00R09
C00R10
C00R11
C00R12
C00R13
C00R14
C00R15
// Column 1
C01R00
C01R01
C01R02
C01R03
C01R04
C01R05
C01R06
C01R07
C01R08
C01R09
C01R10
C01R11
C01R12
C01R13
C01R14
C01R15
// Column 2
C02R00
C02R01
C02R02
C02R03
C02R04
C02R05
C02R06
C02R07
C02R08
C02R09
C02R10
C02R11
C02R12
C02R13
C02R14
C02R15
// Column 3
C03R00
C03R01
C03R02
C03R03
C03R04
C03R05
C03R06
C03R07
C03R08
C03R09
C03R10
C03R11
C03R12
C03R13
C03R14
C03R15
// Column 4
C04R00
C04R01
C04R02
C04R03
C04R04
C04R05
C04R06
C04R07
C04R08
C04R09
C04R10
C04R11
C04R12
C04R13
C04R14
C04R15
// Column 5
C05R00
C05R01
C05R02
C05R03
C05R04
C05R05
C05R06
C05R07
C05R08
C05R09
C05R10
C05R11
C05R12
C05R13
C05R14
C05R15
// Column 6
C06R00
C06R01
C06R02
C06R03
C06R04
C06R05
C06R06
C06R07
C06R08
C06R09
C06R10
C06R11
C06R12
C06R13
C06R14
C06R15
// Column 7
C07R00
C07R01
C07R02
C07R03
C07R04
C07R05
C07R06
C07R07
C07R08
C07R09
C07R10
C07R11
C07R12
C07R13
C07R14
C07R15
// Column 8
C08R00
C08R01
C08R02
C08R03
C08R04
C08R05
C08R06
C08R07
C08R08
C08R09
C08R10
C08R11
C08R12
C08R13
C08R14
C08R15
// Column 9
C09R00
C09R01
C09R02
C09R03
C09R04
C09R05
C09R06
C09R07
C09R08
C09R09
C09R10
C09R11
C09R12
C09R13
C09R14
C09R15
// Column 10
C10R00
C10R01
C10R02
C10R03
C10R04
C10R05
C10R06
C10R07
C10R08
C10R09
C10R10
C10R11
C10R12
C10R13
C10R14
C10R15
// Column 11
C11R00
C11R01
C11R02
C11R03
C11R04
C11R05
C11R06
C11R07
C11R08
C11R09
C11R10
C11R11
C11R12
C11R13
C11R14
C11R15
// Column 12
C12R00
C12R01
C12R02
C12R03
C12R04
C12R05
C12R06
C12R07
C12R08
C12R09
C12R10
C12R11
C12R12
C12R13
C12R14
C12R15
// Column 13
C13R00
C13R01
C13R02
C13R03
C13R04
C13R05
C13R06
C13R07
C13R08
C13R09
C13R10
C13R11
C13R12
C13R13
C13R14
C13R15
// Column 14
C14R00
C14R01
C14R02
C14R03
C14R04
C14R05
C14R06
C14R07
C14R08
C14R09
C14R10
C14R11
C14R12
C14R13
C14R14
C14R15
// Column 15
C15R00
C15R01
C15R02
C15R03
C15R04
C15R05
C15R06
C15R07
C15R08
C15R09
C15R10
C15R11
C15R12
C15R13
C15R14
C15R15
)
// Negative Question Mark.
// var UNKNOWN = NewControlSequence(CSI, C03R07, C06R13, C03R15, CSI, C03R00, C06R13).Bytes()
var UNKNOWN = append(append(SGR(SGRPs.Negative).Bytes(), C03R15), SGR(SGRPs.Positive).Bytes()...)
const (
NUL = C00R00
SOH = C00R01
STX = C00R02
ETX = C00R03
EOT = C00R04
ENQ = C00R05
ACK = C00R06
BEL = C00R07
BS = C00R08
HT = C00R09
LF = C00R10
VT = C00R11
FF = C00R12
CR = C00R13
SO = C00R14
SI = C00R15
DLE = C01R00
DC1 = C01R01 // XON
DC2 = C01R02
DC3 = C01R03 // XOFF
DC4 = C01R04
NAK = C01R05
SYN = C01R06
ETB = C01R07
CAN = C01R08
EM = C01R09
SUB = C01R10
ESC = C01R11
FS = C01R12
GS = C01R13
RS = C01R14
US = C01R15
)
var c0Table controlTable = controlTable{
controlRow{NUL, DLE},
controlRow{SOH, DC1},
controlRow{STX, DC2},
controlRow{ETX, DC3},
controlRow{EOT, DC4},
controlRow{ENQ, NAK},
controlRow{ACK, SYN},
controlRow{BEL, ETB},
controlRow{BS, CAN},
controlRow{HT, EM},
controlRow{LF, SUB},
controlRow{VT, ESC},
controlRow{FF, FS},
controlRow{CR, GS},
controlRow{SO, RS},
controlRow{SI, US},
}
var glTable graphicTable = graphicTable{
graphicRow{C02R00, C03R00, C04R00, C05R00, C06R00, C07R00},
graphicRow{C02R01, C03R01, C04R01, C05R01, C06R01, C07R01},
graphicRow{C02R02, C03R02, C04R02, C05R02, C06R02, C07R02},
graphicRow{C02R03, C03R03, C04R03, C05R03, C06R03, C07R03},
graphicRow{C02R04, C03R04, C04R04, C05R04, C06R04, C07R04},
graphicRow{C02R05, C03R05, C04R05, C05R05, C06R05, C07R05},
graphicRow{C02R06, C03R06, C04R06, C05R06, C06R06, C07R06},
graphicRow{C02R07, C03R07, C04R07, C05R07, C06R07, C07R07},
graphicRow{C02R08, C03R08, C04R08, C05R08, C06R08, C07R08},
graphicRow{C02R09, C03R09, C04R09, C05R09, C06R09, C07R09},
graphicRow{C02R10, C03R10, C04R10, C05R10, C06R10, C07R10},
graphicRow{C02R11, C03R11, C04R11, C05R11, C06R11, C07R11},
graphicRow{C02R12, C03R12, C04R12, C05R12, C06R12, C07R12},
graphicRow{C02R13, C03R13, C04R13, C05R13, C06R13, C07R13},
graphicRow{C02R14, C03R14, C04R14, C05R14, C06R14, C07R14},
graphicRow{C02R15, C03R15, C04R15, C05R15, C06R15, C07R15},
}
const (
IND = C08R04
NEL = C08R05
SSA = C08R06
ESA = C08R07
HTS = C08R08
HTJ = C08R09
VTS = C08R10
PLD = C08R11
PLU = C08R12
RI = C08R13
SS2 = C08R14
SS3 = C08R15
DCS = C09R00
PU1 = C09R01
PU2 = C09R02
STS = C09R03
CCH = C09R04
MW = C09R05
SPA = C09R06
EPA = C09R07
CSI = C09R11
ST = C09R12
OSC = C09R13
PM = C09R14
APC = C09R15
)
var c1Table controlTable = controlTable{
controlRow{C08R00, DCS},
controlRow{C08R01, PU1},
controlRow{C08R02, PU2},
controlRow{C08R03, STS},
controlRow{IND, CCH},
controlRow{NEL, MW},
controlRow{SSA, SPA},
controlRow{ESA, EPA},
controlRow{HTS, C09R08},
controlRow{HTJ, C09R09},
controlRow{VTS, C09R10},
controlRow{PLD, CSI},
controlRow{PLU, ST},
controlRow{RI, OSC},
controlRow{SS2, PM},
controlRow{SS3, APC},
}
var grTable graphicTable = graphicTable{
graphicRow{C10R00, C11R00, C12R00, C13R00, C14R00, C15R00},
graphicRow{C10R01, C11R01, C12R01, C13R01, C14R01, C15R01},
graphicRow{C10R02, C11R02, C12R02, C13R02, C14R02, C15R02},
graphicRow{C10R03, C11R03, C12R03, C13R03, C14R03, C15R03},
graphicRow{C10R04, C11R04, C12R04, C13R04, C14R04, C15R04},
graphicRow{C10R05, C11R05, C12R05, C13R05, C14R05, C15R05},
graphicRow{C10R06, C11R06, C12R06, C13R06, C14R06, C15R06},
graphicRow{C10R07, C11R07, C12R07, C13R07, C14R07, C15R07},
graphicRow{C10R08, C11R08, C12R08, C13R08, C14R08, C15R08},
graphicRow{C10R09, C11R09, C12R09, C13R09, C14R09, C15R09},
graphicRow{C10R10, C11R10, C12R10, C13R10, C14R10, C15R10},
graphicRow{C10R11, C11R11, C12R11, C13R11, C14R11, C15R11},
graphicRow{C10R12, C11R12, C12R12, C13R12, C14R12, C15R12},
graphicRow{C10R13, C11R13, C12R13, C13R13, C14R13, C15R13},
graphicRow{C10R14, C11R14, C12R14, C13R14, C14R14, C15R14},
graphicRow{C10R15, C11R15, C12R15, C13R15, C14R15, C15R15},
}

View File

@ -1,20 +0,0 @@
package vt220
type equivalenceTable map[rune]rune
type EquivalenceTable struct {
et equivalenceTable
}
func (table EquivalenceTable) Normalize(r rune) rune {
if normal, ok := table.et[r]; ok {
return normal
}
return r
}
var Equivalence = EquivalenceTable{
et: equivalenceTable{
'a': 'a',
},
}

View File

@ -1,403 +0,0 @@
package vt220
type Grapheme *rune
func G(r rune) Grapheme {
return &r
}
func N(r *rune) Grapheme {
return r
}
type GraphemeTableRow [6]Grapheme
type GraphemeTable [16]GraphemeTableRow
type lookupTable map[rune][2]int
func (t lookupTable) Has(r rune) bool {
if _, ok := t[r]; ok {
return true
}
return false
}
func (t lookupTable) Get(r rune) *[2]int {
if coords, ok := t[r]; ok {
return &coords
}
return nil
}
type CharacterSet struct {
table GraphemeTable
lookup lookupTable
dscsInter0 *byte
dscsInter1 *byte
dcscFinal byte
decdld *ControlSequence
}
func (set CharacterSet) IsDownLineLoadable() bool {
if set.decdld == nil {
return false
}
return true
}
// Dscs returns the DEC-Select-Character-Set byte sequence for this CharacterSet.
func (set CharacterSet) Dscs() []byte {
dscsLen := 1
if set.dscsInter0 != nil {
if set.dscsInter1 == nil {
panic("Invalid DSCS intermediate 1!")
}
dscsLen = 3
}
dscs := make([]byte, dscsLen, dscsLen)
if dscsLen == 3 {
dscs[0] = *set.dscsInter0
dscs[1] = *set.dscsInter1
}
dscs[dscsLen-1] = set.dcscFinal
return dscs
}
func CharacterSetFromTable(t GraphemeTable, id byte) *CharacterSet {
set := CharacterSet{}
set.table = t
set.dcscFinal = id
set.lookup = lookupTable{}
for i, row := range set.table {
for j, graph := range row {
if graph == nil {
continue
}
set.lookup[*graph] = [2]int{i, j}
}
}
return &set
}
func CharacterSetFromArray(a [94]*rune) *CharacterSet {
gto := CharacterSet{}
gto.lookup = lookupTable{}
var i int
for j, graph := range a {
i = j / 16
gto.table[i][j] = graph
if graph != nil {
gto.lookup[*graph] = [2]int{i, j}
}
}
return &gto
}
var (
mscASCIIGraphic = GraphemeTable{
{G(' '), G('0'), G('@'), G('P'), G('`'), G('p')},
{G('!'), G('1'), G('A'), G('Q'), G('a'), G('q')},
{G('"'), G('2'), G('B'), G('R'), G('b'), G('r')},
{G('#'), G('3'), G('C'), G('S'), G('c'), G('s')},
{G('$'), G('4'), G('D'), G('T'), G('d'), G('t')},
{G('%'), G('5'), G('E'), G('U'), G('e'), G('u')},
{G('&'), G('6'), G('F'), G('V'), G('f'), G('v')},
{G('\''), G('7'), G('G'), G('W'), G('g'), G('w')},
{G('('), G('8'), G('H'), G('X'), G('h'), G('x')},
{G(')'), G('9'), G('I'), G('Y'), G('i'), G('y')},
{G('*'), G(':'), G('J'), G('Z'), G('j'), G('z')},
{G('+'), G(';'), G('K'), G('['), G('k'), G('{')},
{G(','), G('<'), G('L'), G('\\'), G('l'), G('|')},
{G('-'), G('='), G('M'), G(']'), G('m'), G('}')},
{G('.'), G('>'), G('N'), G('^'), G('n'), G('~')},
{G('/'), G('?'), G('O'), G('_'), G('o'), G('\u007F')},
}
mscSupplementalGraphic = GraphemeTable{
{N(nil), G('°'), G('À'), N(nil), G('à'), N(nil)},
{G('¡'), G('±'), G('Á'), G('Ñ'), G('á'), G('ñ')},
{G('¢'), G('²'), G('Â'), G('Ò'), G('â'), G('ò')},
{G('£'), G('³'), G('Ã'), G('Ó'), G('ã'), G('ó')},
{N(nil), N(nil), G('Ä'), G('Ô'), G('ä'), G('ô')},
{G('¥'), G('µ'), G('Å'), G('Õ'), G('å'), G('õ')},
{N(nil), G('¶'), G('Æ'), G('Ö'), G('æ'), G('ö')},
{G('§'), G('·'), G('Ç'), G('Œ'), G('ç'), G('œ')},
{G('¤'), N(nil), G('È'), G('Ø'), G('è'), G('ø')},
{G('©'), G('¹'), G('É'), G('Ù'), G('é'), G('ù')},
{G('ª'), G('º'), G('Ê'), G('Ú'), G('ê'), G('ú')},
{G('«'), G('»'), G('Ë'), G('Û'), G('ë'), G('û')},
{N(nil), G('¼'), G('Ì'), G('Ü'), G('ì'), G('ü')},
{N(nil), G('½'), G('Í'), G('Ÿ'), G('í'), G('ÿ')},
{N(nil), N(nil), G('Î'), N(nil), G('î'), N(nil)},
{N(nil), G('¿'), G('Ï'), G('ß'), G('ï'), N(nil)},
}
specialGraphics = GraphemeTable{
{N(nil), G('0'), G('@'), G('P'), G('♦'), G('⎻')},
{G('!'), G('1'), G('A'), G('Q'), G('█'), G('―')},
{G('"'), G('2'), G('B'), G('R'), G('␉'), G('⎼')},
{G('#'), G('3'), G('C'), G('S'), G('␌'), G('⎽')},
{G('$'), G('4'), G('D'), G('T'), G('␍'), G('├')},
{G('%'), G('5'), G('E'), G('U'), G('␊'), G('┤')},
{G('&'), G('6'), G('F'), G('V'), G('°'), G('┴')},
{G('\''), G('7'), G('G'), G('W'), G('±'), G('┬')},
{G('('), G('8'), G('H'), G('X'), G('␤'), G('│')},
{G(')'), G('9'), G('I'), G('Y'), G('␋'), G('≤')},
{G('*'), G(':'), G('J'), G('Z'), G('┘'), G('≥')},
{G('+'), G(';'), G('K'), G('['), G('┐'), G('π')},
{G(','), G('<'), G('L'), G('\\'), G('┌'), G('≠')},
{G('-'), G('='), G('M'), G(']'), G('└'), G('£')},
{G('.'), G('>'), G('N'), G('^'), G('┼'), G('·')},
{G('/'), G('?'), G('O'), G('\u2800'), G('⎺'), N(nil)},
}
// TODO: Correct to Match docs
nrcBritishBase = GraphemeTable{
{G(' '), G('0'), G('@'), G('P'), G('`'), G('p')},
{G('!'), G('1'), G('A'), G('Q'), G('a'), G('q')},
{G('"'), G('2'), G('B'), G('R'), G('b'), G('r')},
{G('#'), G('3'), G('C'), G('S'), G('c'), G('s')},
{G('$'), G('4'), G('D'), G('T'), G('d'), G('t')},
{G('%'), G('5'), G('E'), G('U'), G('e'), G('u')},
{G('&'), G('6'), G('F'), G('V'), G('f'), G('v')},
{G('\''), G('7'), G('G'), G('W'), G('g'), G('w')},
{G('('), G('8'), G('H'), G('X'), G('h'), G('x')},
{G(')'), G('9'), G('I'), G('Y'), G('i'), G('y')},
{G('*'), G(':'), G('J'), G('Z'), G('j'), G('z')},
{G('+'), G(';'), G('K'), G('['), G('k'), G('{')},
{G(','), G('<'), G('L'), G('\\'), G('l'), G('|')},
{G('-'), G('='), G('M'), G(']'), G('m'), G('}')},
{G('.'), G('>'), G('N'), G('^'), G('n'), G('~')},
{G('/'), G('?'), G('O'), G('_'), G('o'), G('\u007F')},
}
// TODO: Correct to Match docs
nrcDutchBase = GraphemeTable{
{G(' '), G('0'), G('@'), G('P'), G('`'), G('p')},
{G('!'), G('1'), G('A'), G('Q'), G('a'), G('q')},
{G('"'), G('2'), G('B'), G('R'), G('b'), G('r')},
{G('#'), G('3'), G('C'), G('S'), G('c'), G('s')},
{G('$'), G('4'), G('D'), G('T'), G('d'), G('t')},
{G('%'), G('5'), G('E'), G('U'), G('e'), G('u')},
{G('&'), G('6'), G('F'), G('V'), G('f'), G('v')},
{G('\''), G('7'), G('G'), G('W'), G('g'), G('w')},
{G('('), G('8'), G('H'), G('X'), G('h'), G('x')},
{G(')'), G('9'), G('I'), G('Y'), G('i'), G('y')},
{G('*'), G(':'), G('J'), G('Z'), G('j'), G('z')},
{G('+'), G(';'), G('K'), G('['), G('k'), G('{')},
{G(','), G('<'), G('L'), G('\\'), G('l'), G('|')},
{G('-'), G('='), G('M'), G(']'), G('m'), G('}')},
{G('.'), G('>'), G('N'), G('^'), G('n'), G('~')},
{G('/'), G('?'), G('O'), G('_'), G('o'), G('\u007F')},
}
// TODO: Correct to Match docs
nrcFinnishBase = GraphemeTable{
{G(' '), G('0'), G('@'), G('P'), G('`'), G('p')},
{G('!'), G('1'), G('A'), G('Q'), G('a'), G('q')},
{G('"'), G('2'), G('B'), G('R'), G('b'), G('r')},
{G('#'), G('3'), G('C'), G('S'), G('c'), G('s')},
{G('$'), G('4'), G('D'), G('T'), G('d'), G('t')},
{G('%'), G('5'), G('E'), G('U'), G('e'), G('u')},
{G('&'), G('6'), G('F'), G('V'), G('f'), G('v')},
{G('\''), G('7'), G('G'), G('W'), G('g'), G('w')},
{G('('), G('8'), G('H'), G('X'), G('h'), G('x')},
{G(')'), G('9'), G('I'), G('Y'), G('i'), G('y')},
{G('*'), G(':'), G('J'), G('Z'), G('j'), G('z')},
{G('+'), G(';'), G('K'), G('['), G('k'), G('{')},
{G(','), G('<'), G('L'), G('\\'), G('l'), G('|')},
{G('-'), G('='), G('M'), G(']'), G('m'), G('}')},
{G('.'), G('>'), G('N'), G('^'), G('n'), G('~')},
{G('/'), G('?'), G('O'), G('_'), G('o'), G('\u007F')},
}
// TODO: Correct to Match docs
nrcFrenchBase = GraphemeTable{
{G(' '), G('0'), G('@'), G('P'), G('`'), G('p')},
{G('!'), G('1'), G('A'), G('Q'), G('a'), G('q')},
{G('"'), G('2'), G('B'), G('R'), G('b'), G('r')},
{G('#'), G('3'), G('C'), G('S'), G('c'), G('s')},
{G('$'), G('4'), G('D'), G('T'), G('d'), G('t')},
{G('%'), G('5'), G('E'), G('U'), G('e'), G('u')},
{G('&'), G('6'), G('F'), G('V'), G('f'), G('v')},
{G('\''), G('7'), G('G'), G('W'), G('g'), G('w')},
{G('('), G('8'), G('H'), G('X'), G('h'), G('x')},
{G(')'), G('9'), G('I'), G('Y'), G('i'), G('y')},
{G('*'), G(':'), G('J'), G('Z'), G('j'), G('z')},
{G('+'), G(';'), G('K'), G('['), G('k'), G('{')},
{G(','), G('<'), G('L'), G('\\'), G('l'), G('|')},
{G('-'), G('='), G('M'), G(']'), G('m'), G('}')},
{G('.'), G('>'), G('N'), G('^'), G('n'), G('~')},
{G('/'), G('?'), G('O'), G('_'), G('o'), G('\u007F')},
}
// TODO: Correct to Match docs
nrcFrenchCanadianBase = GraphemeTable{
{G(' '), G('0'), G('@'), G('P'), G('`'), G('p')},
{G('!'), G('1'), G('A'), G('Q'), G('a'), G('q')},
{G('"'), G('2'), G('B'), G('R'), G('b'), G('r')},
{G('#'), G('3'), G('C'), G('S'), G('c'), G('s')},
{G('$'), G('4'), G('D'), G('T'), G('d'), G('t')},
{G('%'), G('5'), G('E'), G('U'), G('e'), G('u')},
{G('&'), G('6'), G('F'), G('V'), G('f'), G('v')},
{G('\''), G('7'), G('G'), G('W'), G('g'), G('w')},
{G('('), G('8'), G('H'), G('X'), G('h'), G('x')},
{G(')'), G('9'), G('I'), G('Y'), G('i'), G('y')},
{G('*'), G(':'), G('J'), G('Z'), G('j'), G('z')},
{G('+'), G(';'), G('K'), G('['), G('k'), G('{')},
{G(','), G('<'), G('L'), G('\\'), G('l'), G('|')},
{G('-'), G('='), G('M'), G(']'), G('m'), G('}')},
{G('.'), G('>'), G('N'), G('^'), G('n'), G('~')},
{G('/'), G('?'), G('O'), G('_'), G('o'), G('\u007F')},
}
// TODO: Correct to Match docs
nrcGermanBase = GraphemeTable{
{G(' '), G('0'), G('@'), G('P'), G('`'), G('p')},
{G('!'), G('1'), G('A'), G('Q'), G('a'), G('q')},
{G('"'), G('2'), G('B'), G('R'), G('b'), G('r')},
{G('#'), G('3'), G('C'), G('S'), G('c'), G('s')},
{G('$'), G('4'), G('D'), G('T'), G('d'), G('t')},
{G('%'), G('5'), G('E'), G('U'), G('e'), G('u')},
{G('&'), G('6'), G('F'), G('V'), G('f'), G('v')},
{G('\''), G('7'), G('G'), G('W'), G('g'), G('w')},
{G('('), G('8'), G('H'), G('X'), G('h'), G('x')},
{G(')'), G('9'), G('I'), G('Y'), G('i'), G('y')},
{G('*'), G(':'), G('J'), G('Z'), G('j'), G('z')},
{G('+'), G(';'), G('K'), G('['), G('k'), G('{')},
{G(','), G('<'), G('L'), G('\\'), G('l'), G('|')},
{G('-'), G('='), G('M'), G(']'), G('m'), G('}')},
{G('.'), G('>'), G('N'), G('^'), G('n'), G('~')},
{G('/'), G('?'), G('O'), G('_'), G('o'), G('\u007F')},
}
// TODO: Correct to Match docs
nrcItalianBase = GraphemeTable{
{G(' '), G('0'), G('@'), G('P'), G('`'), G('p')},
{G('!'), G('1'), G('A'), G('Q'), G('a'), G('q')},
{G('"'), G('2'), G('B'), G('R'), G('b'), G('r')},
{G('#'), G('3'), G('C'), G('S'), G('c'), G('s')},
{G('$'), G('4'), G('D'), G('T'), G('d'), G('t')},
{G('%'), G('5'), G('E'), G('U'), G('e'), G('u')},
{G('&'), G('6'), G('F'), G('V'), G('f'), G('v')},
{G('\''), G('7'), G('G'), G('W'), G('g'), G('w')},
{G('('), G('8'), G('H'), G('X'), G('h'), G('x')},
{G(')'), G('9'), G('I'), G('Y'), G('i'), G('y')},
{G('*'), G(':'), G('J'), G('Z'), G('j'), G('z')},
{G('+'), G(';'), G('K'), G('['), G('k'), G('{')},
{G(','), G('<'), G('L'), G('\\'), G('l'), G('|')},
{G('-'), G('='), G('M'), G(']'), G('m'), G('}')},
{G('.'), G('>'), G('N'), G('^'), G('n'), G('~')},
{G('/'), G('?'), G('O'), G('_'), G('o'), G('\u007F')},
}
// TODO: Correct to Match docs
nrcNorwegianDanishBase = GraphemeTable{
{G(' '), G('0'), G('@'), G('P'), G('`'), G('p')},
{G('!'), G('1'), G('A'), G('Q'), G('a'), G('q')},
{G('"'), G('2'), G('B'), G('R'), G('b'), G('r')},
{G('#'), G('3'), G('C'), G('S'), G('c'), G('s')},
{G('$'), G('4'), G('D'), G('T'), G('d'), G('t')},
{G('%'), G('5'), G('E'), G('U'), G('e'), G('u')},
{G('&'), G('6'), G('F'), G('V'), G('f'), G('v')},
{G('\''), G('7'), G('G'), G('W'), G('g'), G('w')},
{G('('), G('8'), G('H'), G('X'), G('h'), G('x')},
{G(')'), G('9'), G('I'), G('Y'), G('i'), G('y')},
{G('*'), G(':'), G('J'), G('Z'), G('j'), G('z')},
{G('+'), G(';'), G('K'), G('['), G('k'), G('{')},
{G(','), G('<'), G('L'), G('\\'), G('l'), G('|')},
{G('-'), G('='), G('M'), G(']'), G('m'), G('}')},
{G('.'), G('>'), G('N'), G('^'), G('n'), G('~')},
{G('/'), G('?'), G('O'), G('_'), G('o'), G('\u007F')},
}
// TODO: Correct to Match docs
nrcSpanishBase = GraphemeTable{
{G(' '), G('0'), G('@'), G('P'), G('`'), G('p')},
{G('!'), G('1'), G('A'), G('Q'), G('a'), G('q')},
{G('"'), G('2'), G('B'), G('R'), G('b'), G('r')},
{G('#'), G('3'), G('C'), G('S'), G('c'), G('s')},
{G('$'), G('4'), G('D'), G('T'), G('d'), G('t')},
{G('%'), G('5'), G('E'), G('U'), G('e'), G('u')},
{G('&'), G('6'), G('F'), G('V'), G('f'), G('v')},
{G('\''), G('7'), G('G'), G('W'), G('g'), G('w')},
{G('('), G('8'), G('H'), G('X'), G('h'), G('x')},
{G(')'), G('9'), G('I'), G('Y'), G('i'), G('y')},
{G('*'), G(':'), G('J'), G('Z'), G('j'), G('z')},
{G('+'), G(';'), G('K'), G('['), G('k'), G('{')},
{G(','), G('<'), G('L'), G('\\'), G('l'), G('|')},
{G('-'), G('='), G('M'), G(']'), G('m'), G('}')},
{G('.'), G('>'), G('N'), G('^'), G('n'), G('~')},
{G('/'), G('?'), G('O'), G('_'), G('o'), G('\u007F')},
}
// TODO: Correct to Match docs
nrcSwedishBase = GraphemeTable{
{G(' '), G('0'), G('@'), G('P'), G('`'), G('p')},
{G('!'), G('1'), G('A'), G('Q'), G('a'), G('q')},
{G('"'), G('2'), G('B'), G('R'), G('b'), G('r')},
{G('#'), G('3'), G('C'), G('S'), G('c'), G('s')},
{G('$'), G('4'), G('D'), G('T'), G('d'), G('t')},
{G('%'), G('5'), G('E'), G('U'), G('e'), G('u')},
{G('&'), G('6'), G('F'), G('V'), G('f'), G('v')},
{G('\''), G('7'), G('G'), G('W'), G('g'), G('w')},
{G('('), G('8'), G('H'), G('X'), G('h'), G('x')},
{G(')'), G('9'), G('I'), G('Y'), G('i'), G('y')},
{G('*'), G(':'), G('J'), G('Z'), G('j'), G('z')},
{G('+'), G(';'), G('K'), G('['), G('k'), G('{')},
{G(','), G('<'), G('L'), G('\\'), G('l'), G('|')},
{G('-'), G('='), G('M'), G(']'), G('m'), G('}')},
{G('.'), G('>'), G('N'), G('^'), G('n'), G('~')},
{G('/'), G('?'), G('O'), G('_'), G('o'), G('\u007F')},
}
// TODO: Correct to Match docs
nrcSwissBase = GraphemeTable{
{G(' '), G('0'), G('@'), G('P'), G('`'), G('p')},
{G('!'), G('1'), G('A'), G('Q'), G('a'), G('q')},
{G('"'), G('2'), G('B'), G('R'), G('b'), G('r')},
{G('#'), G('3'), G('C'), G('S'), G('c'), G('s')},
{G('$'), G('4'), G('D'), G('T'), G('d'), G('t')},
{G('%'), G('5'), G('E'), G('U'), G('e'), G('u')},
{G('&'), G('6'), G('F'), G('V'), G('f'), G('v')},
{G('\''), G('7'), G('G'), G('W'), G('g'), G('w')},
{G('('), G('8'), G('H'), G('X'), G('h'), G('x')},
{G(')'), G('9'), G('I'), G('Y'), G('i'), G('y')},
{G('*'), G(':'), G('J'), G('Z'), G('j'), G('z')},
{G('+'), G(';'), G('K'), G('['), G('k'), G('{')},
{G(','), G('<'), G('L'), G('\\'), G('l'), G('|')},
{G('-'), G('='), G('M'), G(']'), G('m'), G('}')},
{G('.'), G('>'), G('N'), G('^'), G('n'), G('~')},
{G('/'), G('?'), G('O'), G('_'), G('o'), G('\u007F')},
}
)
type MSC struct {
ASCIIGraphic *GraphemeTable
SupplementalGraphic *GraphemeTable
}
type NRC struct {
British *GraphemeTable
Dutch *GraphemeTable
Finnish *GraphemeTable
French *GraphemeTable
FrenchCanadian *GraphemeTable
German *GraphemeTable
Italian *GraphemeTable
NorwegianDanish *GraphemeTable
Spanish *GraphemeTable
Swedish *GraphemeTable
Swiss *GraphemeTable
}
var DEC = struct {
MSC MSC
SpecialGraphics *GraphemeTable
NRC NRC
}{
MSC: MSC{
ASCIIGraphic: &mscASCIIGraphic,
SupplementalGraphic: &mscSupplementalGraphic,
},
SpecialGraphics: &specialGraphics,
NRC: NRC{
British: &nrcBritishBase,
Dutch: &nrcDutchBase,
Finnish: &nrcFinnishBase,
French: &nrcFrenchBase,
FrenchCanadian: &nrcFrenchCanadianBase,
German: &nrcGermanBase,
Italian: &nrcItalianBase,
NorwegianDanish: &nrcNorwegianDanishBase,
Spanish: &nrcSpanishBase,
Swedish: &nrcSwedishBase,
Swiss: &nrcSwissBase,
},
}

View File

@ -1,292 +0,0 @@
package vt220
import (
"bytes"
"errors"
"fmt"
"io"
requiv "git.sdf.org/CRThaze/vtTools/requivalence"
)
type graphicRegister struct {
dcs ControlSequencePrefix
set *CharacterSet
designated bool
}
type GraphicRegister struct {
register *graphicRegister
}
type VT220 struct {
writer io.Writer
c0 ControlRange
gl GraphicRange
c1 ControlRange
gr GraphicRange
graphicRegistry [4]GraphicRegister
graphicRepetoire [15]*CharacterSet
fontBuffer **CharacterSet
}
func NewVT220(w io.Writer) *VT220 {
return &VT220{
writer: w,
c0: ControlRange{
table: c0Table,
colOffset: 0,
set: rangeLookup{
'\a': &c0Table[7][0],
'\b': &c0Table[8][0],
'\t': &c0Table[9][0],
'\n': &c0Table[10][0],
'\v': &c0Table[11][0],
'\f': &c0Table[12][0],
'\r': &c0Table[13][0],
},
},
gl: GraphicRange{
table: glTable,
colOffset: 2,
},
c1: ControlRange{
table: c1Table,
colOffset: 8,
},
gr: GraphicRange{
table: grTable,
colOffset: 10,
},
}
}
func (vt *VT220) Init() {
vt.graphicRepetoire = [15]*CharacterSet{
CharacterSetFromTable(mscASCIIGraphic, C04R02),
CharacterSetFromTable(mscSupplementalGraphic, C03R12),
CharacterSetFromTable(specialGraphics, C03R00),
CharacterSetFromTable(nrcBritishBase, C04R01),
CharacterSetFromTable(nrcDutchBase, C03R04),
CharacterSetFromTable(nrcFinnishBase, C04R03),
CharacterSetFromTable(nrcFrenchBase, C05R02),
CharacterSetFromTable(nrcFrenchCanadianBase, C05R01),
CharacterSetFromTable(nrcGermanBase, C04R11),
CharacterSetFromTable(nrcItalianBase, C05R09),
CharacterSetFromTable(nrcNorwegianDanishBase, C04R05),
CharacterSetFromTable(nrcSpanishBase, C05R10),
CharacterSetFromTable(nrcSwedishBase, C04R08),
CharacterSetFromTable(nrcSwissBase, C03R13),
nil,
}
vt.fontBuffer = &vt.graphicRepetoire[14]
vt.graphicRegistry = [4]GraphicRegister{
{&graphicRegister{dcs: DesignateCharSetR0CtrlPfx}},
{&graphicRegister{dcs: DesignateCharSetR1CtrlPfx}},
{&graphicRegister{dcs: DesignateCharSetR2CtrlPfx}},
{&graphicRegister{dcs: DesignateCharSetR3CtrlPfx}},
}
vt.gl.lockShifts = map[GraphicRegister]ControlSequence{
vt.graphicRegistry[0]: ShiftCtrl.LockShiftG0,
vt.graphicRegistry[1]: ShiftCtrl.LockShiftG1,
vt.graphicRegistry[2]: ShiftCtrl.LockShiftG2,
vt.graphicRegistry[3]: ShiftCtrl.LockShiftG3,
}
vt.gl.singleShifts = map[GraphicRegister]ControlSequence{
vt.graphicRegistry[2]: ShiftCtrl.SingleShiftG2,
vt.graphicRegistry[3]: ShiftCtrl.SingleShiftG3,
}
vt.gr.lockShifts = map[GraphicRegister]ControlSequence{
vt.graphicRegistry[1]: ShiftCtrl.LockShiftG1Right,
vt.graphicRegistry[2]: ShiftCtrl.LockShiftG2Right,
vt.graphicRegistry[3]: ShiftCtrl.LockShiftG3Right,
}
vt.gl.load(vt.graphicRepetoire[0])
vt.gr.load(vt.graphicRepetoire[1])
vt.DesignateCharSet(0, 0)
vt.DesignateCharSet(1, 1)
vt.DesignateCharSet(2, 2)
}
func (vt *VT220) DesignateCharSet(grIndex, register int) error {
if grIndex < 0 || grIndex > 14 {
return errors.New("Invalid Graphic Repetoire Index")
}
if register < 0 || register > 3 {
return errors.New("Invalid Graphic Register")
}
// Invoke designation control sequence.
if _, err := vt.InvokeCtrlSequence(
vt.graphicRegistry[register].register.dcs.With(vt.graphicRepetoire[grIndex].Dscs()...),
); err != nil {
return err
}
vt.graphicRegistry[register].register.set = vt.graphicRepetoire[grIndex]
vt.graphicRegistry[register].register.designated = true
return nil
}
func (vt *VT220) InvokeCtrlSequence(cs ControlSequence) (n int, err error) {
return vt.writer.Write(cs.Bytes())
}
func (vt *VT220) LockShift(registerIndex int, right bool) error {
if registerIndex < 0 || registerIndex > 3 {
return errors.New("Invalid Graphic Register")
}
rg := vt.gl
if right {
rg = vt.gr
}
return rg.lockShift(vt.graphicRegistry[registerIndex], vt.writer)
}
func (vt *VT220) SingleShiftRune(registerIndex int, r rune) ([]byte, error) {
if registerIndex < 2 || registerIndex > 3 {
return nil, errors.New("Invalid Graphic Register")
}
coords := vt.graphicRegistry[registerIndex].register.set.lookup.Get(r)
if coords == nil {
return nil, errors.New("Rune does not map to the character set in the given register.")
}
codepoint, err := vt.gl.Get(coords[1]+int(vt.gl.colOffset), coords[0])
if err != nil {
return nil, err
}
bs, err := vt.gl.singleShift(vt.graphicRegistry[registerIndex])
if err != nil {
return nil, err
}
bs = append(bs, codepoint)
return bs, err
}
func (vt *VT220) LoadDownLineCharacterSet(set *CharacterSet, w io.Writer) error {
// TODO: Validate overlay (94 symbols max)
if set.decdld == nil {
return errors.New("CharacterSet has no DECDLD sequence.")
}
w.Write(set.decdld.Bytes())
*vt.fontBuffer = set
return nil
}
func (t VT220) TermID() string {
return "vt220"
}
// Trans takes a rune and finds the matching code point in either the GL or the GR graphics table.
// The code-point is then returned as a string.
// If no matching code-point could be found, then an error is returned.
func (vt *VT220) Trans(r rune) ([]byte, error) {
ba := [1]byte{}
if b := vt.c0.Lookup(r); b != nil {
ba[0] = *b
} else if b := vt.gl.Lookup(r); b != nil {
ba[0] = *b
} else if b := vt.gr.Lookup(r); b != nil {
ba[0] = *b
} else {
return nil, errors.New("No translation for rune.")
}
return ba[:], nil
}
// MustTrans takes a rune and calls the Trans function. if the Trans function returns an error then
// MustTrans returns the code-point for a question mark on the default graphics set (0x3F).
func (vt *VT220) MustTrans(r rune) []byte {
if bs, err := vt.Trans(r); err == nil {
return bs
}
bs := UNKNOWN
return bs[:]
}
// SafeTrans uses an equivalency table to normalize the given rune if it is visually similar
// enough to a unicode character mapped in the Graphic Repetoire.
// It then calls MustTrans with the normalized rune.
func (vt *VT220) SafeTrans(r rune) []byte {
return vt.MustTrans(Equivalence.Normalize(r))
}
// TransDynamic first calls Trans. If Trans returns an error, then it attempts to find the rune
// anywhere within the GraphicRepetoir. If it is able to find the rune, it adds the necessary
// escape sequences to temporarily load the needed character set to properly display the matching
// symbol. If none can be found, it retuns the same fall-back code-point as MustTrans.
func (vt *VT220) TransDynamic(r rune) []byte {
if s, err := vt.Trans(r); err == nil {
return s
}
if vt.graphicRegistry[2].register.designated {
if vt.graphicRegistry[2].register.set.lookup.Has(r) {
if bs, err := vt.SingleShiftRune(2, r); err == nil {
return bs
} else {
panic(err)
}
}
}
if vt.graphicRegistry[3].register.designated {
if vt.graphicRegistry[3].register.set.lookup.Has(r) {
if bs, err := vt.SingleShiftRune(3, r); err == nil {
return bs
} else {
panic(err)
}
}
}
for i, set := range vt.graphicRepetoire {
if set == nil || !set.lookup.Has(r) {
continue
}
// TODO: Don't leave this in the register.
if err := vt.DesignateCharSet(i, 3); err != nil {
panic(err)
}
if bs, err := vt.SingleShiftRune(3, r); err == nil {
return bs
} else {
panic(err)
}
}
return UNKNOWN[:]
}
// SafeTrans uses an equivalency table for the given rune to determine a similiar rune that is
// present in the GraphicRepetoir. It then calls TransDynamic with that rune.
func (vt *VT220) SafeTransDynamic(r rune) []byte {
return vt.TransDynamic(requiv.Equivalence.Normalize(r))
}
func (vt VT220) write(buf *bytes.Buffer) (n int, err error) {
for r, _, e := buf.ReadRune(); e != io.EOF; r, _, e = buf.ReadRune() {
if e != nil {
err = e
return
}
var bn int
bn, err = vt.writer.Write(vt.SafeTransDynamic(r))
n += bn
if err != nil {
return
}
}
return
}
func (t VT220) Write(p []byte) (n int, err error) {
return t.write(bytes.NewBuffer(p))
}
func (t VT220) Print(a ...any) (n int, err error) {
return t.write(bytes.NewBufferString(fmt.Sprint(a...)))
}
func (t VT220) Printf(format string, a ...any) (n int, err error) {
return t.Print(fmt.Sprintf(format, a...))
}
func (t VT220) Println(a ...any) (n int, err error) {
return t.Print(fmt.Sprintln(a...))
}