// Copyright (C) 2017 Keelan Lightfoot // Copyright (C) 2007-2008 Board of Regents of the University of Wisconsin // System (Univ. of Wisconsin-Madison, Trace R&D Center) // Copyright (C) 2007-2008 Omnitor AB // Copyright (C) 2007-2008 Voiceriver Inc // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation; either version 2.1 of the License, or (at // your option) any later version. package gortty import ( "bytes" "context" "fmt" ) // Encoder is fun type Encoder struct { input chan rune output chan byte autoWrap bool // true for auto CRLF mode autoCRLF bool charset *CodeSet crlfThresh int // start looking for a space to replace with a crlf at this threshold crlfForceThresh int // force a crlf if it hasn't happend by this threshold shiftThresh int callback Callback } // NewEncoder is fun func NewEncoder(ctx context.Context, input chan rune, output chan byte, charset *CodeSet) *Encoder { e := new(Encoder) if input == nil { e.input = make(chan rune) } else { e.input = input } e.output = output e.charset = charset e.crlfThresh = 60 e.crlfForceThresh = 72 e.shiftThresh = 70 go e.run(ctx) return e } // EncodeString is fun func (e *Encoder) EncodeString(s string) { for _, c := range s { e.input <- c } } // SetAutoCRLF is fun func (e *Encoder) SetAutoCRLF(autoCRLF bool) { e.autoCRLF = autoCRLF } // SetCallback is fun func (e *Encoder) SetCallback(callback Callback) { e.callback = callback } func (e *Encoder) run(ctx context.Context) { var modCase int // current Letters/Figures state var charsWithoutCRLF int // number of chars we have handled without inserting a CR-LF var charsWithoutShift int // number of chars we have handled without seeing a shift for { select { case <-ctx.Done(): return case r := <-e.input: c := e.charset.toCode(r) /*this rune can either be rendered as a Figures character, a Letters character or WHITESPACE which can be rendered by either character set*/ if r == '\r' || r == '\n' { charsWithoutCRLF = 0 } /*see if we need to first insert a case character*/ if c.shift == shiftWhitespace { /*we have white space, we don't need to insert a Letters/Figures case character. We will insert a case character when we transition from white space to some other case. The only exception to this is when we are just starting to transmit, in which case we must send something, so we'll choose the more common Letters case*/ if modCase == shiftUnknown { e.output <- e.charset.unshift() } modCase = shiftWhitespace } else if c.shift != modCase { /*case is either Letters or Figures and doesn't match our modulator's current state, so add a new case char here*/ if c.shift == shiftLetter { e.output <- e.charset.unshift() } else { e.output <- e.charset.shift() } /*and save our current state*/ modCase = c.shift /*and update our counter*/ //fmt.Println("charsWithoutShift", charsWithoutShift) charsWithoutShift = 0 } /*we need to perform a little logic here to check for some * boundry conditions in the protocol*/ /* We need to ensure that we re-send the current case character at least every 72 characters. */ if charsWithoutShift > e.shiftThresh { if modCase == shiftWhitespace { e.output <- e.charset.unshift() /*doesn't matter which, so how 'bout LETR*/ } else if modCase == shiftLetter { e.output <- e.charset.unshift() } else { e.output <- e.charset.shift() } //fmt.Println("charsWithoutShift", charsWithoutShift) charsWithoutShift = 0 } /* we need to ensure that we send a carriage return/linefeed * at least every 72 characters, but we start looking for a * space character starting at 60 characters */ if !e.autoWrap { /*we just insert our regular character*/ e.output <- c.toByte() if e.autoCRLF { if r == '\n' { e.output <- e.charset.toByte('\r') } else if r == '\r' { e.output <- e.charset.toByte('\n') } } } else if (charsWithoutCRLF > e.crlfThresh) && (modCase == shiftWhitespace) { /*insert the cr-lf instead of the space character*/ e.output <- e.charset.toByte('\r') e.output <- e.charset.toByte('\n') charsWithoutCRLF = 0 } else if charsWithoutCRLF > e.crlfForceThresh { /*insert the cr-lf followed by the character*/ e.output <- e.charset.toByte('\r') e.output <- e.charset.toByte('\n') e.output <- c.toByte() charsWithoutCRLF = 0 } else { /*we just insert our regular character*/ e.output <- c.toByte() if e.autoCRLF { if r == '\n' { e.output <- e.charset.toByte('\r') } else if r == '\r' { e.output <- e.charset.toByte('\n') } } } charsWithoutShift++ charsWithoutCRLF++ } } } func (e *Encoder) Write(p []byte) (n int, err error) { runes := bytes.Runes(p) fmt.Print(string(runes)) for _, r := range runes { e.input <- r } return len(p), nil }