Documentation ¶
Overview ¶
Package bbs is a Go module that interacts with legacy textfiles encoded with Bulletin Board Systems (BBS) color codes to reconstruct them into HTML documents.
BBSes were popular in the 1980s and 1990s and allowed computer users to chat, message, and share files over the landline telephone network. The commercialization and ease of access to the Internet eventually replaced BBSes, as did the worldwide-web. These centralized systems, termed boards, used a text-based interface, and their owners often applied colorization, text themes, and art to differentiate themselves.
While in the 1990s, ANSI control codes were in everyday use on the PC/MS-DOS, the standard comes from mainframe equipment. Home microcomputers often had difficulty interpreting it. So, BBS developers created their own, more straightforward methods to colorize and theme the text output to solve this.
*Please note that many microcomputer, PC and MS-DOS based boards used ANSI control codes for colorizations that this library does not support.
PCBoard ¶
One of the most well-known applications for hosting a PC/MS-DOS BBS, PCBoard pioneered the file_id.diz file descriptor, as well as being endlessly expandable through software plugins known as PPEs. It developed the popular @X color code and @ control syntax.
Celerity ¶
Another PC/MS-DOS application that was very popular with the hacking, phreaking, and pirate communities in the early 1990s. It introduced a unique | pipe code syntax in late 1991 that revised the code syntax in version 2 of the software.
Renegade ¶
A PC/MS-DOS application that was a derivative of the source code of Telegard BBS. Surprisingly there was a new release of this software in 2021. Renegade had two methods to implement color, and this library uses the Pipe Bar Color Codes.
Telegard ¶
A PC/MS-DOS application became famous due to a source code leak or release by one of its authors back in an era when most developers were still highly secretive with their code. The source is incorporated into several other projects.
WVIV ¶
A mainstay in the PC/MS-DOS BBS scene of the 1980s and early 1990s, it became well known for releasing its source code to registered users. It allowed them to expand the code to incorporate additional software such as games or utilities and port it to other platforms. The source is now Open Source and is still updated. Confusingly WWIV has three methods of colorizing text, 10 Pipe colors, two-digit pipe colors, and its original Heart Codes.
Wildcat ¶
WILDCAT! was a popular, propriety PC/MS-DOS application from the late 1980s that later migrated to Windows. It was one of the few BBS applications that sold at retail in a physical box. It extensively used @ color codes throughout later revisions of its software.
Example ¶
package main import ( "bytes" "embed" "fmt" "log" "github.com/bengarrett/bbs" "golang.org/x/text/encoding/charmap" "golang.org/x/text/transform" ) //go:embed static/* var static embed.FS func main() { // print about the file file, err := static.Open("static/examples/hello.pcb") if err != nil { log.Fatal(err) } defer file.Close() s, name, err := bbs.Fields(file) if err != nil { log.Print(err) return } fmt.Printf("Found %d %s color controls.\n\n", len(s), name) // reopen the file file, err = static.Open("static/examples/hello.pcb") if err != nil { log.Print(err) return } defer file.Close() // transform the MS-DOS legacy text to Unicode decoder := charmap.CodePage437.NewDecoder() reader := transform.NewReader(file, decoder) // create the HTML equivalent of BBS color codes var buf bytes.Buffer if _, err := bbs.HTML(&buf, reader); err != nil { log.Print(err) return } fmt.Print(buf.String()) }
Output: Found 11 PCBoard @X color controls. <i class="PB0 PFF"> </i><i class="PB7 PF0"> ┌─────────────┐ </i><i class="PB0 PF7"> </i><i class="PB0 PFF"> </i><i class="PB7 PF0"> │ Hello </i><i class="PBF PF0">world </i><i class="PB7 PF0">│ </i><i class="PB0 PF7"> </i><i class="PB0 PFF"> </i><i class="PB7 PF0"> └─────────────┘ </i><i class="PB0 PF7"></i>
Index ¶
- Constants
- Variables
- func CelerityHTML(dst *bytes.Buffer, src []byte) error
- func IsCelerity(src []byte) bool
- func IsPCBoard(src []byte) bool
- func IsRenegade(src []byte) bool
- func IsTelegard(src []byte) bool
- func IsWWIVHash(src []byte) bool
- func IsWWIVHeart(src []byte) bool
- func IsWildcat(src []byte) bool
- func PCBoardHTML(dst *bytes.Buffer, src []byte) error
- func RenegadeHTML(dst *bytes.Buffer, src []byte) error
- func TelegardHTML(dst *bytes.Buffer, src []byte) error
- func TrimControls(src []byte) []byte
- func WWIVHashHTML(dst *bytes.Buffer, src []byte) error
- func WWIVHeartHTML(dst *bytes.Buffer, src []byte) error
- func WildcatHTML(dst *bytes.Buffer, src []byte) error
- type BBS
Examples ¶
- Package
- BBS.Bytes
- BBS.CSS
- BBS.HTML
- BBS.HTML (Ansi)
- BBS.HTML (Find)
- BBS.Name
- BBS.Remove
- BBS.Remove (Find)
- BBS.String
- BBS.Valid
- BBS.Valid (Ansi)
- BBS.Valid (False)
- CelerityHTML
- Fields
- Fields (Ansi)
- Fields (None)
- Find
- Find (Ansi)
- Find (None)
- HTML
- IsCelerity
- IsPCBoard
- IsRenegade
- IsTelegard
- IsWWIVHash
- IsWWIVHeart
- IsWildcat
- PCBoardHTML
- RenegadeHTML
- TrimControls
Constants ¶
const ( CelerityRe string = `\|(k|b|g|c|r|m|y|w|d|B|G|C|R|M|Y|W|S)` // matches Celerity PCBoardRe string = "(?i)@X([0-9A-F][0-9A-F])" // matches PCBoard RenegadeRe string = `\|(0[0-9]|1[1-9]|2[0-3])` // matches Renegade TelegardRe string = "(?i)`([0-9|A-F])([0-9|A-F])" // matches Telegard WildcatRe string = `(?i)@([0-9|A-F])([0-9|A-F])@` // matches Wildcat! WWIVHashRe string = `\|#(\d)` // matches WWIV with hashes # WWIVHeartRe string = `\x03(\d)` // matches WWIV with hearts ♥ )
Regular expressions to match BBS color codes.
const (
Clear string = "@CLS@"
)
Clear is a PCBoard specific control to clear the screen that's occasionally found in ANSI text.
Variables ¶
var ( ErrANSI = errors.New("ansi escape code found") ErrNone = errors.New("no bbs color code found") )
Generic text match errors. Errors returned can be tested against these errors using errors.Is.
var (
ErrBuff = errors.New("bytes buffer cannot be nil")
)
Syntax errors.
Functions ¶
func CelerityHTML ¶ added in v1.0.0
CelerityHTML writes to dst the HTML equivalent of Celerity BBS color codes with matching CSS color classes.
Example ¶
package main import ( "bytes" "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("|cHello |C|S|wworld") var buf bytes.Buffer if err := bbs.CelerityHTML(&buf, src); err != nil { fmt.Print(err) } fmt.Print(buf.String()) }
Output: <i class="PBk PFc">Hello </i><i class="PBk PFC"></i><i class="PBw PFC">world</i>
func IsCelerity ¶ added in v1.0.0
IsCelerity reports if the bytes contains Celerity BBS color codes. The format uses the vertical bar (|) followed by a case sensitive single alphabetic character.
Example ¶
package main import ( "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("|cHello |C|S|wworld") fmt.Print(bbs.IsCelerity(src)) }
Output: true
func IsPCBoard ¶ added in v1.0.0
IsPCBoard reports if the bytes contains PCBoard BBS color codes. The format uses an at-sign x (@X) prefix with a background and foreground, 4-bit hexadecimal color value.
Example ¶
package main import ( "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("@X03Hello world") fmt.Print(bbs.IsPCBoard(src)) }
Output: true
func IsRenegade ¶ added in v1.0.0
IsRenegade reports if the bytes contains Renegade BBS color codes. The format uses the vertical bar (|) followed by a padded, numeric value between 00 and 23.
Example ¶
package main import ( "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("|03Hello |07|19world") fmt.Print(bbs.IsRenegade(src)) }
Output: true
func IsTelegard ¶ added in v1.0.0
IsTelegard reports if the bytes contains Telegard BBS color codes. The format uses the grave accent (`) followed by a padded, numeric value between 00 and 23.
Example ¶
package main import ( "fmt" "github.com/bengarrett/bbs" ) func main() { const grave = "\u0060" // godoc treats a grave character as a special control src := []byte(grave + "7Hello world") fmt.Print(bbs.IsTelegard(src)) }
Output: true
func IsWWIVHash ¶ added in v1.0.0
IsWWIVHash reports if the bytes contains WWIV BBS hash color codes. The format uses a vertical bar (|) with the hash (#) characters as a prefix with a numeric value between 0 and 9.
Example ¶
package main import ( "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("|#7Hello world") fmt.Print(bbs.IsWWIVHash(src)) }
Output: true
func IsWWIVHeart ¶ added in v1.0.0
IsWWIVHeart reports if the bytes contains WWIV BBS heart (♥) color codes. The format uses the ETX (end-of-text) character as a prefix with a numeric value between 0 and 9.
In the MS-DOS era, the common North American CP-437 codepage substituted the ETX character with a heart symbol.
Example ¶
package main import ( "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("\x037Hello world") fmt.Print(bbs.IsWWIVHeart(src)) }
Output: true
func IsWildcat ¶ added in v1.0.0
IsWildcat reports if the bytes contains Wildcat! BBS color codes. The format uses an a background and foreground, 4-bit hexadecimal color value enclosed with two at-sign (@) characters.
Example ¶
package main import ( "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("@0F@Hello world") fmt.Print(bbs.IsWildcat(src)) }
Output: true
func PCBoardHTML ¶ added in v1.0.0
PCBoardHTML writes to dst the HTML equivalent of PCBoard BBS color codes with matching CSS color classes.
Example ¶
package main import ( "bytes" "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("@X03Hello world") var buf bytes.Buffer if err := bbs.PCBoardHTML(&buf, src); err != nil { fmt.Print(err) } fmt.Print(buf.String()) }
Output: <i class="PB0 PF3">Hello world</i>
func RenegadeHTML ¶ added in v1.0.0
RenegadeHTML writes to dst the HTML equivalent of Renegade BBS color codes with matching CSS color classes.
Example ¶
package main import ( "bytes" "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("|03Hello |07|19world") var buf bytes.Buffer if err := bbs.RenegadeHTML(&buf, src); err != nil { fmt.Print(err) } fmt.Print(buf.String()) }
Output: <i class="P0 P3">Hello </i><i class="P0 P7"></i><i class="P19 P7">world</i>
func TelegardHTML ¶ added in v1.0.0
TelegardHTML writes to dst the HTML equivalent of Telegard BBS color codes with matching CSS color classes.
func TrimControls ¶
TrimControls removes common PCBoard BBS controls prefixes from the bytes. It trims the @CLS@ prefix used to clear the screen and the @PAUSE@ prefix used to pause the display render.
Example ¶
package main import ( "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("@CLS@@PAUSE@Hello world") fmt.Printf("%q trims to %s", src, string(bbs.TrimControls(src))) }
Output: "@CLS@@PAUSE@Hello world" trims to Hello world
func WWIVHashHTML ¶ added in v1.0.0
WWIVHashHTML writes to dst the HTML equivalent of WWIV BBS hash (#) color codes with matching CSS color classes.
func WWIVHeartHTML ¶ added in v1.0.0
WWIVHeartHTML writes to dst the HTML equivalent of WWIV BBS heart (♥) color codes with matching CSS color classes.
Types ¶
type BBS ¶
type BBS int
A BBS (Bulletin Board System) color code format, other than for Find, the ANSI BBS is not supported by this library.
const ( ANSI BBS = iota // ANSI escape sequence. Celerity // Celerity pipe. PCBoard // PCBoard @ sign. Renegade // Renegade pipe. Telegard // Telegard grave accent. Wildcat // Wildcat! @ sign. WWIVHash // WWIV # symbol. WWIVHeart // WWIV ♥ symbol. )
BBS codes and sequences.
func Fields ¶
Fields splits the io.Reader around the first instance of one or more consecutive BBS color codes. An error is returned if no color codes are found or if ANSI control sequences are first found.
Example ¶
package main import ( "fmt" "log" "strings" "github.com/bengarrett/bbs" ) func main() { r := strings.NewReader("@X03Hello @XF0world") s, b, err := bbs.Fields(r) if err != nil { log.Print(err) } fmt.Printf("Found %d, %s sequences\n", len(s), b) for i, item := range s { fmt.Printf("Sequence %d: %q\n", i+1, item) } }
Output: Found 2, PCBoard @X sequences Sequence 1: "03Hello " Sequence 2: "F0world"
Example (Ansi) ¶
package main import ( "errors" "fmt" "strings" "github.com/bengarrett/bbs" ) func main() { const reset = "\x1b[0m" // an ANSI escape sequence to reset the terminal r := strings.NewReader(reset + "Hello world") s, b, err := bbs.Fields(r) if errors.Is(err, bbs.ErrANSI) { fmt.Printf("error: %s", err) return } fmt.Printf("Found %d, %s sequences\n", len(s), b) }
Output: error: ansi escape code found
Example (None) ¶
package main import ( "errors" "fmt" "strings" "github.com/bengarrett/bbs" ) func main() { r := strings.NewReader("Hello world") s, b, err := bbs.Fields(r) if errors.Is(err, bbs.ErrNone) { fmt.Printf("error: %s", err) return } fmt.Printf("Found %d, %s sequences\n", len(s), b) }
Output: error: no bbs color code found
func Find ¶
Find the format of any known BBS color code sequence within the reader. If no sequences are found -1 is returned.
Example ¶
package main import ( "fmt" "strings" "github.com/bengarrett/bbs" ) func main() { src := strings.NewReader("@X03Hello world") f := bbs.Find(src) fmt.Printf("Found %s text", f.Name()) }
Output: Found PCBoard text
Example (Ansi) ¶
package main import ( "fmt" "strings" "github.com/bengarrett/bbs" ) func main() { const reset = "\x1b[0m" // an ANSI escape sequence to reset the terminal src := strings.NewReader(reset + "Hello world") f := bbs.Find(src) if !f.Valid() { fmt.Print("Found plain text") return } fmt.Printf("Found %s text", f.Name()) }
Output: Found ANSI text
Example (None) ¶
package main import ( "fmt" "strings" "github.com/bengarrett/bbs" ) func main() { src := strings.NewReader("Hello world") f := bbs.Find(src) if !f.Valid() { fmt.Print("Found plain text") return } fmt.Printf("Found %s text", f.Name()) }
Output: Found plain text
func HTML ¶
HTML writes to dst the HTML equivalent of BBS color codes with matching CSS color classes. The first found color code format is used for the remainder of the Reader.
Example ¶
package main import ( "bytes" "fmt" "strings" "github.com/bengarrett/bbs" ) func main() { src := strings.NewReader("@X03Hello world") var buf bytes.Buffer r, err := bbs.HTML(&buf, src) if err != nil { fmt.Print(err) return } fmt.Printf("<!-- %s code -->\n", r) fmt.Print(buf.String()) }
Output: <!-- PCBoard @X code --> <i class="PB0 PF3">Hello world</i>
func (BBS) Bytes ¶
Bytes returns the BBS color toggle sequence.
Example ¶
package main import ( "fmt" "github.com/bengarrett/bbs" ) func main() { b := bbs.PCBoard.Bytes() fmt.Printf("Code as bytes %v\n", b) fmt.Printf("Code as string %s", b) }
Output: Code as bytes [64 88] Code as string @X
func (BBS) CSS ¶
CSS writes to dst the Cascading Style Sheets classes needed by the HTML.
The CSS results rely on custom properties which are not supported by legacy browsers.
Example ¶
package main import ( "bytes" "fmt" "strings" "github.com/bengarrett/bbs" ) func main() { var css bytes.Buffer if err := bbs.PCBoard.CSS(&css); err != nil { fmt.Print(err) } // print the first 8 lines of the css lines := strings.Split(css.String(), "\n") for i := 0; i < 8; i++ { fmt.Println(lines[i]) } }
Output: @import url("text_bbs.css"); @import url("text_blink.css"); /* PCBoard and WildCat! BBS colours */ i.PF0 { color: var(--black); }
func (BBS) HTML ¶
HTML writes to dst the BBS color codes as CSS color classes within HTML <i> elements.
Example ¶
package main import ( "bytes" "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("@X03Hello @X04world@X00") var buf bytes.Buffer if err := bbs.PCBoard.HTML(&buf, src); err != nil { fmt.Print(err) return } fmt.Print(buf.String()) }
Output: <i class="PB0 PF3">Hello </i><i class="PB0 PF4">world</i><i class="PB0 PF0"></i>
Example (Ansi) ¶
package main import ( "bytes" "fmt" "github.com/bengarrett/bbs" ) func main() { const reset = "\x1b[0m" // an ANSI escape sequence to reset the terminal src := []byte(reset + "Hello world") result := bbs.Find(bytes.NewReader(src)) var buf bytes.Buffer if err := result.HTML(&buf, src); err != nil { fmt.Printf("error: %s", err) return } fmt.Print(buf.String()) }
Output: error: ansi escape code found
Example (Find) ¶
package main import ( "bytes" "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("@X03Hello @X04world@X00") result := bbs.Find(bytes.NewReader(src)) var buf bytes.Buffer if err := result.HTML(&buf, src); err != nil { fmt.Print(err) return } fmt.Print(buf.String()) }
Output: <i class="PB0 PF3">Hello </i><i class="PB0 PF4">world</i><i class="PB0 PF0"></i>
func (BBS) Name ¶
Name returns the name of the BBS color format.
Example ¶
package main import ( "fmt" "github.com/bengarrett/bbs" ) func main() { fmt.Print(bbs.PCBoard.Name()) }
Output: PCBoard
func (BBS) Remove ¶
Remove the BBS color codes from src and write it to dst.
Example ¶
package main import ( "bytes" "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("@X03Hello @X07world") var buf bytes.Buffer if err := bbs.PCBoard.Remove(&buf, src); err != nil { fmt.Print(err) } fmt.Printf("%q to %s", src, buf.String()) }
Output: "@X03Hello @X07world" to Hello world
Example (Find) ¶
package main import ( "bytes" "fmt" "github.com/bengarrett/bbs" ) func main() { src := []byte("@X03Hello @X07world") result := bbs.Find(bytes.NewReader(src)) var buf bytes.Buffer if err := result.Remove(&buf, src); err != nil { fmt.Print(err) } fmt.Printf("%q to %s", src, buf.String()) }
Output: "@X03Hello @X07world" to Hello world
func (BBS) String ¶
String returns the BBS color format name and toggle sequence.
Example ¶
package main import ( "fmt" "github.com/bengarrett/bbs" ) func main() { fmt.Print(bbs.PCBoard) }
Output: PCBoard @X
func (BBS) Valid ¶
Valid reports whether the BBS type is valid.
Example ¶
package main import ( "fmt" "strings" "github.com/bengarrett/bbs" ) func main() { src := "@X03Hello @X07world" r := strings.NewReader(src) ok := bbs.Find(r).Valid() fmt.Print(ok) }
Output: true
Example (Ansi) ¶
package main import ( "fmt" "strings" "github.com/bengarrett/bbs" ) func main() { const reset = "\x1b[0m" // an ANSI escape sequence to reset the terminal src := reset + "Hello world" r := strings.NewReader(src) ok := bbs.Find(r).Valid() fmt.Print(ok) }
Output: true
Example (False) ¶
package main import ( "fmt" "strings" "github.com/bengarrett/bbs" ) func main() { src := "Hello world" r := strings.NewReader(src) ok := bbs.Find(r).Valid() fmt.Print(ok) }
Output: false