diff --git a/consoleout/console_adm3a.go b/consoleout/console_adm3a.go index 26fd95e..018262b 100644 --- a/consoleout/console_adm3a.go +++ b/consoleout/console_adm3a.go @@ -1,6 +1,10 @@ package consoleout -import "fmt" +import ( + "fmt" + "io" + "os" +) // Adm3AOutputDriver holds our state. type Adm3AOutputDriver struct { @@ -13,6 +17,9 @@ type Adm3AOutputDriver struct { // y stores the cursor Y y uint8 + + // writer is where we send our output + writer io.Writer } // GetName returns the name of this driver. @@ -31,40 +38,40 @@ func (a3a *Adm3AOutputDriver) PutCharacter(c uint8) { case 0: switch c { case 0x07: /* BEL: flash screen */ - fmt.Printf("\033[?5h\033[?5l") + fmt.Fprintf(a3a.writer, "\033[?5h\033[?5l") case 0x7F: /* DEL: echo BS, space, BS */ - fmt.Printf("\b \b") + fmt.Fprintf(a3a.writer, "\b \b") case 0x1A: /* adm3a clear screen */ - fmt.Printf("\033[H\033[2J") + fmt.Fprintf(a3a.writer, "\033[H\033[2J") case 0x0C: /* vt52 clear screen */ - fmt.Printf("\033[H\033[2J") + fmt.Fprintf(a3a.writer, "\033[H\033[2J") case 0x1E: /* adm3a cursor home */ - fmt.Printf("\033[H") + fmt.Fprintf(a3a.writer, "\033[H") case 0x1B: a3a.status = 1 /* esc-prefix */ case 1: a3a.status = 2 /* cursor motion prefix */ case 2: /* insert line */ - fmt.Printf("\033[L") + fmt.Fprintf(a3a.writer, "\033[L") case 3: /* delete line */ - fmt.Printf("\033[M") + fmt.Fprintf(a3a.writer, "\033[M") case 0x18, 5: /* clear to eol */ - fmt.Printf("\033[K") + fmt.Fprintf(a3a.writer, "\033[K") case 0x12, 0x13: // nop default: - fmt.Printf("%c", c) + fmt.Fprintf(a3a.writer, "%c", c) } case 1: /* we had an esc-prefix */ switch c { case 0x1B: - fmt.Printf("%c", c) + fmt.Fprintf(a3a.writer, "%c", c) case '=', 'Y': a3a.status = 2 case 'E': /* insert line */ - fmt.Printf("\033[L") + fmt.Fprintf(a3a.writer, "\033[L") case 'R': /* delete line */ - fmt.Printf("\033[M") + fmt.Fprintf(a3a.writer, "\033[M") case 'B': /* enable attribute */ a3a.status = 4 case 'C': /* disable attribute */ @@ -75,7 +82,7 @@ func (a3a *Adm3AOutputDriver) PutCharacter(c uint8) { a3a.status = 8 default: /* some true ANSI sequence? */ a3a.status = 0 - fmt.Printf("%c%c", 0x1B, c) + fmt.Fprintf(a3a.writer, "%c%c", 0x1B, c) } case 2: a3a.y = c - ' ' + 1 @@ -83,50 +90,50 @@ func (a3a *Adm3AOutputDriver) PutCharacter(c uint8) { case 3: a3a.x = c - ' ' + 1 a3a.status = 0 - fmt.Printf("\033[%d;%dH", a3a.y, a3a.x) + fmt.Fprintf(a3a.writer, "\033[%d;%dH", a3a.y, a3a.x) case 4: /* +B prefix */ a3a.status = 0 switch c { case '0': /* start reverse video */ - fmt.Printf("\033[7m") + fmt.Fprintf(a3a.writer, "\033[7m") case '1': /* start half intensity */ - fmt.Printf("\033[1m") + fmt.Fprintf(a3a.writer, "\033[1m") case '2': /* start blinking */ - fmt.Printf("\033[5m") + fmt.Fprintf(a3a.writer, "\033[5m") case '3': /* start underlining */ - fmt.Printf("\033[4m") + fmt.Fprintf(a3a.writer, "\033[4m") case '4': /* cursor on */ - fmt.Printf("\033[?25h") + fmt.Fprintf(a3a.writer, "\033[?25h") case '5': /* video mode on */ // nop case '6': /* remember cursor position */ - fmt.Printf("\033[s") + fmt.Fprintf(a3a.writer, "\033[s") case '7': /* preserve status line */ // nop default: - fmt.Printf("%cB%c", 0x1B, c) + fmt.Fprintf(a3a.writer, "%cB%c", 0x1B, c) } case 5: /* +C prefix */ a3a.status = 0 switch c { case '0': /* stop reverse video */ - fmt.Printf("\033[27m") + fmt.Fprintf(a3a.writer, "\033[27m") case '1': /* stop half intensity */ - fmt.Printf("\033[m") + fmt.Fprintf(a3a.writer, "\033[m") case '2': /* stop blinking */ - fmt.Printf("\033[25m") + fmt.Fprintf(a3a.writer, "\033[25m") case '3': /* stop underlining */ - fmt.Printf("\033[24m") + fmt.Fprintf(a3a.writer, "\033[24m") case '4': /* cursor off */ - fmt.Printf("\033[?25l") + fmt.Fprintf(a3a.writer, "\033[?25l") case '6': /* restore cursor position */ - fmt.Printf("\033[u") + fmt.Fprintf(a3a.writer, "\033[u") case '5': /* video mode off */ // nop case '7': /* don't preserve status line */ // nop default: - fmt.Printf("%cC%c", 0x1B, c) + fmt.Fprintf(a3a.writer, "%cC%c", 0x1B, c) } /* set/clear line/point */ case 6: @@ -141,9 +148,16 @@ func (a3a *Adm3AOutputDriver) PutCharacter(c uint8) { } +// SetWriter will update the writer. +func (a3a *Adm3AOutputDriver) SetWriter(w io.Writer) { + a3a.writer = w +} + // init registers our driver, by name. func init() { Register("adm-3a", func() ConsoleDriver { - return &Adm3AOutputDriver{} + return &Adm3AOutputDriver{ + writer: os.Stdout, + } }) } diff --git a/consoleout/console_ansi.go b/consoleout/console_ansi.go index 3142179..07e44fb 100644 --- a/consoleout/console_ansi.go +++ b/consoleout/console_ansi.go @@ -1,9 +1,15 @@ package consoleout -import "fmt" +import ( + "fmt" + "io" + "os" +) // AnsiOutputDriver holds our state. type AnsiOutputDriver struct { + // writer is where we send our output + writer io.Writer } // GetName returns the name of this driver. @@ -17,12 +23,19 @@ func (ad *AnsiOutputDriver) GetName() string { // // This is part of the OutputDriver interface. func (ad *AnsiOutputDriver) PutCharacter(c uint8) { - fmt.Printf("%c", c) + fmt.Fprintf(ad.writer, "%c", c) +} + +// SetWriter will update the writer. +func (ad *AnsiOutputDriver) SetWriter(w io.Writer) { + ad.writer = w } // init registers our driver, by name. func init() { Register("ansi", func() ConsoleDriver { - return &AnsiOutputDriver{} + return &AnsiOutputDriver{ + writer: os.Stdout, + } }) } diff --git a/consoleout/console_null.go b/consoleout/console_null.go new file mode 100644 index 0000000..eb4ac27 --- /dev/null +++ b/consoleout/console_null.go @@ -0,0 +1,43 @@ +package consoleout + +import ( + "io" + "os" +) + +// NullOutputDriver holds our state. +type NullOutputDriver struct { + + // writer is where we send our output + writer io.Writer +} + +// GetName returns the name of this driver. +// +// This is part of the OutputDriver interface. +func (no *NullOutputDriver) GetName() string { + return "null" +} + +// PutCharacter writes the specified character to the console, +// as this is a null-driver nothing happens and instead the output +// is discarded. +// +// This is part of the OutputDriver interface. +func (n *NullOutputDriver) PutCharacter(c uint8) { + // NOTHING HAppens +} + +// SetWriter will update the writer. +func (n *NullOutputDriver) SetWriter(w io.Writer) { + n.writer = w +} + +// init registers our driver, by name. +func init() { + Register("null", func() ConsoleDriver { + return &NullOutputDriver{ + writer: os.Stdout, + } + }) +} diff --git a/consoleout/console_test.go b/consoleout/console_test.go index 0ec2919..64e8e61 100644 --- a/consoleout/console_test.go +++ b/consoleout/console_test.go @@ -1,6 +1,9 @@ package consoleout -import "testing" +import ( + "bytes" + "testing" +) // TestName ensures we can lookup a driver by name func TestName(t *testing.T) { @@ -52,3 +55,57 @@ func TestChangeDriver(t *testing.T) { t.Fatalf("driver changed unexpectedly") } } + +// TestOutput ensures that our two "real" drivers output, as expected +func TestOutput(t *testing.T) { + + // Drivers that should produce output + valid := []string{"ansi", "adm-3a"} + + for _, nm := range valid { + + d, e := New(nm) + if e != nil { + t.Fatalf("failed to lookup driver by name %s:%s", nm, e) + } + + // ensure we redirect the output + tmp := &bytes.Buffer{} + + d.driver.SetWriter(tmp) + + for _, c := range "Steve Kemp" { + d.PutCharacter(byte(c)) + } + + // Test we got the output we expected + if tmp.String() != "Steve Kemp" { + t.Fatalf("output driver %s produced '%s'", d.GetName(), tmp.String()) + } + } + +} + +// TestNull ensures nothing is written by the null output driver +func TestNull(t *testing.T) { + + // Start with a known-good driver + null, err := New("null") + if err != nil { + t.Fatalf("failed to load starting driver %s", err) + } + if null.GetName() != "null" { + t.Fatalf("null driver has the wrong name") + } + + // ensure we redirect the output + tmp := &bytes.Buffer{} + + null.driver.SetWriter(tmp) + + null.PutCharacter('s') + + if tmp.String() != "" { + t.Fatalf("got output, expected none: '%s'", tmp.String()) + } +} diff --git a/consoleout/consoleout.go b/consoleout/consoleout.go index 3430009..b877464 100644 --- a/consoleout/consoleout.go +++ b/consoleout/consoleout.go @@ -5,7 +5,10 @@ // given just a name. package consoleout -import "fmt" +import ( + "fmt" + "io" +) // ConsoleDriver is the interface that must be implemented by anything // that wishes to be used as a console driver. @@ -19,6 +22,9 @@ type ConsoleDriver interface { // GetName will return the name of the driver. GetName() string + + // SetWriter will update the writer + SetWriter(io.Writer) } // This is a map of known-drivers