123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- // 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
- }
|