package main import ( "bytes" "errors" "fmt" "io" ) var ( VT220Type = TermType{"vt220"} ) type graphicRegister struct { dcs ControlSequencePrefix set *CharacterSet } type GraphicRegister struct { register *graphicRegister } type VT220 struct { writer io.Writer C0 ControlRange GL GraphicRange C1 ControlRange GR GraphicRange G0 GraphicRegister G1 GraphicRegister G2 GraphicRegister G3 GraphicRegister 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.GL.lockShifts = map[GraphicRegister]ControlSequence{ vt.G0: ShiftCtrl.LockShiftG0, vt.G1: ShiftCtrl.LockShiftG1, vt.G2: ShiftCtrl.LockShiftG2, vt.G3: ShiftCtrl.LockShiftG3, } vt.GL.singleShifts = map[GraphicRegister]ControlSequence{ vt.G2: ShiftCtrl.SingleShiftG2, vt.G3: ShiftCtrl.SingleShiftG3, } vt.GR.lockShifts = map[GraphicRegister]ControlSequence{ vt.G1: ShiftCtrl.LockShiftG1Right, vt.G2: ShiftCtrl.LockShiftG2Right, vt.G3: ShiftCtrl.LockShiftG3Right, } 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{ {dcs: DesignateCharSetR0CtrlPfx}, {dcs: DesignateCharSetR1CtrlPfx}, {dcs: DesignateCharSetR2CtrlPfx}, {dcs: DesignateCharSetR3CtrlPfx}, } 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. vt.graphicRegistry[register].dcs.With(vt.graphicRepetoire[grIndex].Dscs()...).Invoke(vt.writer) vt.graphicRegistry[register].set = vt.graphicRepetoire[grIndex] return nil } 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 := TransRune(r); err == nil { // return s // } // if G2.register.set.lookup.Has(r) { // if ba, err := GL.SingleShift(G2, r); err == nil { // return string(ba[:]) // } else { // panic(err) // } // } // if G3.register.set.lookup.Has(r) { // if ba, err := GL.SingleShift(G3, r); err == nil { // return string(ba[:]) // } else { // panic(err) // } // } // if G1.register.set.lookup.Has(r) { // // prevG2set := G2.register.set // // G2.Load() // } // for _, set := range GraphicRepetoir.gr { // if _, ok := set.lookup[r]; !ok { // continue // } // if *set.table[0][0] == r { // // TODO: load // } // } // // TODO: return UNKNOWN // // return string(ba[:]) return vt.MustTrans(r) } // 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(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...)) }