From db52566c577500c1f2a6f8b16dd30969b6046e25 Mon Sep 17 00:00:00 2001 From: Ben Garrett Date: Wed, 10 Jan 2024 13:20:57 +1100 Subject: [PATCH] Fixed VBarsHTML returning invalid values. Docs update. --- README.md | 63 ++++------ bbs.go | 250 ++++++++++++++++++------------------- example_test.go | 4 +- examplebbs_test.go | 266 ++++++++++++++++++++++++++-------------- examplefields_test.go | 36 +++++- internal/split/split.go | 4 +- 6 files changed, 365 insertions(+), 258 deletions(-) diff --git a/README.md b/README.md index c783bf6..348df67 100644 --- a/README.md +++ b/README.md @@ -3,53 +3,52 @@ Package bbs is a [Go module](https://go.dev/) 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, +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 world-wide-web. - -These centralized systems, termed boards, used a text-based interface, and their +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](https://en.wikipedia.org/wiki/ANSI_escape_code) were in everyday use on the [PC/MS-DOS](https://en.wikipedia.org/wiki/MS-DOS), the standard comes from mainframe equipment. Home microcomputers often had difficulty -interpreting it. So BBS developers created their own, more straightforward methods +interpreting it. So, BBS developers created their own, more straightforward methods to colorize and theme the text output to solve this. -*Please note, while many PC/MS-DOS boards used ANSI control codes for colorizations, -this library does not support the standard. +*Please note that many microcomputer, PC and MS-DOS based boards used ANSI control codes for colorizations that this library does not support. ## Quick usage [Go Package with docs and examples.](https://pkg.go.dev/github.com/bengarrett/bbs) ```go -// open a text file +// open the text file file, err := os.Open("pcboard.txt") if err != nil { - log.Fatal(err) + log.Print(err) + return } defer file.Close() -// transform the MS-DOS legacy text to Unicode +// transform the MS-DOS text to Unicode decoder := charmap.CodePage437.NewDecoder() reader := transform.NewReader(file, decoder) // create the HTML equivalent of BBS color codes -var htm bytes.Buffer -match, err := bbs.HTML(&htm, reader) +var buf bytes.Buffer +cc, err := bbs.HTML(&buf, reader) if err != nil { - log.Fatal(err) + log.Print(err) + return } // fetch CSS var css bytes.Buffer -if err := match.CSS(&css); err != nil { - log.Fatal(err) +if err := cc.CSS(&css); err != nil { + log.Print(err) + return } -// print partial html and css -fmt.Println(htm.String()) -fmt.Println(css.String()) +// print the partial html and css +fmt.Fprintln(os.Stdout, css.String(), "\n", buf.String()) ``` ## Known codes @@ -57,40 +56,32 @@ fmt.Println(css.String()) ### 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. +pioneered the `file_id.diz` file descriptor, and 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 +Another PC/MS-DOS application 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 +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. +one of its authors in an era when most developers were still highly +secretive with their code. The source is in use in several other BBS applications. ### WWIV -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. +A mainstay in the PC/MS-DOS BBS scene of the 1980s and early 1990s, the software became well-known for releasing its source code to registered users. It allowed owners 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. \ No newline at end of file +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 sold at retail in a physical box. It extensively used **@** color codes throughout later revisions of the software. \ No newline at end of file diff --git a/bbs.go b/bbs.go index 89d72a8..670a344 100644 --- a/bbs.go +++ b/bbs.go @@ -1,20 +1,20 @@ -// Package bbs interacts with legacy textfiles encoded with -// Bulletin Board Systems (BBS) color codes to reconstruct them into HTML documents. +// 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 world-wide-web. +// 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. // -// 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. // -// 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, while many PC/MS-DOS boards used ANSI control codes for colorizations, -// this library does not support the standard. +// *Please note that many microcomputer, PC and MS-DOS based boards used ANSI control +// codes for colorizations that this library does not support. // // # PCBoard // @@ -56,6 +56,9 @@ // 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. +// +// [Bulletin Board Systems]: https://spectrum.ieee.org/social-medias-dialup-ancestor-the-bulletin-board-system +// [ANSI control codes]: https://www.cse.psu.edu/~kxc104/class/cse472/09f/hw/hw7/vt100ansi.htm package bbs import ( @@ -71,55 +74,36 @@ import ( "github.com/bengarrett/bbs/internal/split" ) +// Generic text match errors. +// Errors returned can be tested against these errors using errors.Is. var ( ErrANSI = errors.New("ansi escape code found") + ErrNone = errors.New("no bbs color code found") +) + +// Syntax errors. +var ( ErrBuff = errors.New("bytes buffer cannot be nil") - ErrNone = errors.New("no bbs color codes found") ) //go:embed static/* var static embed.FS -// Bulletin Board System color code format. -// Other than for Find, the ANSI type is not supported by this library. -type BBS int - +// Regular expressions to match BBS color codes. const ( - ANSI BBS = iota // ANSI escape sequences. - Celerity // Celerity BBS pipe codes. - PCBoard // PCBoard BBS @ codes. - Renegade // Renegade BBS pipe codes. - Telegard // Telegard BBS grave accent codes. - Wildcat // Wildcat! BBS @ codes. - WWIVHash // WWIV BBS # codes. - WWIVHeart // WWIV BBS ♥ codes. + 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 ♥ ) +// Clear is a PCBoard specific control to clear the screen that's occasionally found in ANSI text. const ( - // Clear is a PCBoard specific control to clear the screen that's occasionally found in ANSI text. Clear string = "@CLS@" - // CelerityRe is a regular expression to match Celerity BBS color codes. - CelerityRe string = `\|(k|b|g|c|r|m|y|w|d|B|G|C|R|M|Y|W|S)` - - // PCBoardMatch is a case-insensitive, regular expression to match PCBoard BBS color codes. - PCBoardRe string = "(?i)@X([0-9A-F][0-9A-F])" - - // RenegadeRe is a regular expression to match Renegade BBS color codes. - RenegadeRe string = `\|(0[0-9]|1[1-9]|2[0-3])` - - // TelegardRe is a case-insensitive, regular expression to match Telegard BBS color codes. - TelegardRe string = "(?i)`([0-9|A-F])([0-9|A-F])" - - // WildcatRe is a case-insensitive, regular expression to match Wildcat! BBS color codes. - WildcatRe string = `(?i)@([0-9|A-F])([0-9|A-F])@` - - // WWIVHashRe is a regular expression to match WWIV BBS # color codes. - WWIVHashRe string = `\|#(\d)` - - // WWIVHeartRe is a regular expression to match WWIV BBS ♥ color codes. - WWIVHeartRe string = `\x03(\d)` - celerityCodes = "kbgcrmywdBGCRMYWS" ) @@ -141,26 +125,6 @@ func RenegadeHTML(dst *bytes.Buffer, src []byte) error { return split.VBarsHTML(dst, src) } -// PCBoardHTML writes to dst the HTML equivalent of PCBoard BBS color codes with -// matching CSS color classes. -func PCBoardHTML(dst *bytes.Buffer, src []byte) error { - if dst == nil { - return ErrBuff - } - return split.PCBoardHTML(dst, src) -} - -// TelegardHTML writes to dst the HTML equivalent of Telegard BBS color codes with -// matching CSS color classes. -func TelegardHTML(dst *bytes.Buffer, src []byte) error { - if dst == nil { - return ErrBuff - } - r := regexp.MustCompile(TelegardRe) - x := r.ReplaceAll(src, []byte(`@X$1$2`)) - return split.PCBoardHTML(dst, x) -} - // WildcatHTML writes to dst the HTML equivalent of Wildcat! BBS color codes with // matching CSS color classes. func WildcatHTML(dst *bytes.Buffer, src []byte) error { @@ -172,30 +136,8 @@ func WildcatHTML(dst *bytes.Buffer, src []byte) error { return split.PCBoardHTML(dst, x) } -// WWIVHashHTML writes to dst the HTML equivalent of WWIV BBS # color codes with -// matching CSS color classes. -func WWIVHashHTML(dst *bytes.Buffer, src []byte) error { - if dst == nil { - return ErrBuff - } - r := regexp.MustCompile(WWIVHashRe) - x := r.ReplaceAll(src, []byte(`|0$1`)) - return split.VBarsHTML(dst, x) -} - -// WWIVHeartHTML writes to dst the HTML equivalent of WWIV BBS ♥ color codes with -// matching CSS color classes. -func WWIVHeartHTML(dst *bytes.Buffer, src []byte) error { - if dst == nil { - return ErrBuff - } - r := regexp.MustCompile(WWIVHeartRe) - x := r.ReplaceAll(src, []byte(`|0$1`)) - return split.VBarsHTML(dst, x) -} - // IsCelerity reports if the bytes contains Celerity BBS color codes. -// The format uses the vertical bar "|" followed by a case sensitive single alphabetic character. +// The format uses the vertical bar (|) followed by a case sensitive single alphabetic character. func IsCelerity(src []byte) bool { // celerityCodes contains all the character sequences for Celerity. for _, code := range []byte(celerityCodes) { @@ -207,7 +149,7 @@ func IsCelerity(src []byte) bool { } // IsPCBoard reports if the bytes contains PCBoard BBS color codes. -// The format uses an "@X" prefix with a background and foreground, 4-bit hexadecimal color value. +// The format uses an at-sign x (@X) prefix with a background and foreground, 4-bit hexadecimal color value. func IsPCBoard(src []byte) bool { const first, last = 0, 15 const hexxed = "%X%X" @@ -224,7 +166,7 @@ func IsPCBoard(src []byte) bool { } // 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. +// The format uses the vertical bar (|) followed by a padded, numeric value between 00 and 23. func IsRenegade(src []byte) bool { const first, last = 0, 23 const leadingZero = "%01d" @@ -239,7 +181,7 @@ func IsRenegade(src []byte) bool { } // 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. +// The format uses the grave accent (`) followed by a padded, numeric value between 00 and 23. func IsTelegard(src []byte) bool { const first, last = 0, 23 const leadingZero = "%01d" @@ -253,25 +195,8 @@ func IsTelegard(src []byte) bool { return false } -// IsWildcat reports if the bytes contains Wildcat! BBS color codes. -// The format uses an a background and foreground, -// 4-bit hexadecimal color value enclosed by two at "@" characters. -func IsWildcat(src []byte) bool { - const first, last = 0, 15 - for bg := first; bg <= last; bg++ { - for fg := first; fg <= last; fg++ { - subslice := []byte(fmt.Sprintf("%s%X%X%s", - Wildcat.Bytes(), bg, fg, Wildcat.Bytes())) - if bytes.Contains(src, subslice) { - return true - } - } - } - return false -} - -// IsWWIVHash reports if the bytes contains WWIV BBS # (hash or pound) color codes. -// The format uses a vertical bar "|" with the hash "#" characters +// 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. func IsWWIVHash(src []byte) bool { const first, last = 0, 9 @@ -284,10 +209,12 @@ func IsWWIVHash(src []byte) bool { return false } -// IsWWIVHeart reports if the bytes contains WWIV BBS ♥ (heart) color codes. -// The format uses the ETX character as a prefix with a numeric value between 0 and 9. -// In the standard MS-DOS, USA codepage (CP-437), the ETX (end-of-text) -// character is substituted with a heart character. +// 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. +// +// [CP-437 codepage]: https://en.wikipedia.org/wiki/Code_page_437 func IsWWIVHeart(src []byte) bool { const first, last = 0, 9 for i := first; i <= last; i++ { @@ -299,14 +226,89 @@ func IsWWIVHeart(src []byte) bool { return false } +// 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. +func IsWildcat(src []byte) bool { + const first, last = 0, 15 + for bg := first; bg <= last; bg++ { + for fg := first; fg <= last; fg++ { + subslice := []byte(fmt.Sprintf("%s%X%X%s", + Wildcat.Bytes(), bg, fg, Wildcat.Bytes())) + if bytes.Contains(src, subslice) { + return true + } + } + } + return false +} + +// PCBoardHTML writes to dst the HTML equivalent of PCBoard BBS color codes with +// matching CSS color classes. +func PCBoardHTML(dst *bytes.Buffer, src []byte) error { + if dst == nil { + return ErrBuff + } + return split.PCBoardHTML(dst, src) +} + +// TelegardHTML writes to dst the HTML equivalent of Telegard BBS color codes with +// matching CSS color classes. +func TelegardHTML(dst *bytes.Buffer, src []byte) error { + if dst == nil { + return ErrBuff + } + r := regexp.MustCompile(TelegardRe) + x := r.ReplaceAll(src, []byte(`@X$1$2`)) + return split.PCBoardHTML(dst, x) +} + // TrimControls removes common PCBoard BBS controls prefixes from the bytes. -// It trims the "@CLS@" prefix used to clear the screen and the "@PAUSE@" prefix +// It trims the @CLS@ prefix used to clear the screen and the @PAUSE@ prefix // used to pause the display render. func TrimControls(src []byte) []byte { r := regexp.MustCompile(`@(CLS|CLS |PAUSE)@`) return r.ReplaceAll(src, []byte("")) } +// WWIVHashHTML writes to dst the HTML equivalent of WWIV BBS hash (#) color codes with +// matching CSS color classes. +func WWIVHashHTML(dst *bytes.Buffer, src []byte) error { + if dst == nil { + return ErrBuff + } + r := regexp.MustCompile(WWIVHashRe) + x := r.ReplaceAll(src, []byte(`|0$1`)) + return split.VBarsHTML(dst, x) +} + +// WWIVHeartHTML writes to dst the HTML equivalent of WWIV BBS heart (♥) color codes with +// matching CSS color classes. +func WWIVHeartHTML(dst *bytes.Buffer, src []byte) error { + if dst == nil { + return ErrBuff + } + r := regexp.MustCompile(WWIVHeartRe) + x := r.ReplaceAll(src, []byte(`|0$1`)) + return split.VBarsHTML(dst, x) +} + +// A BBS (Bulletin Board System) color code format, +// other than for [Find], the [ANSI] BBS is not supported by this library. +type BBS int + +// BBS codes and sequences. +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. +) + // 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. func Fields(src io.Reader) ([]string, BBS, error) { @@ -391,7 +393,7 @@ func HTML(dst *bytes.Buffer, src io.Reader) (BBS, error) { return find, find.HTML(dst, b) } -// Bytes returns the BBS color toggle sequence as bytes. +// Bytes returns the BBS color toggle sequence. func (b BBS) Bytes() []byte { const ( etx byte = 3 // CP437 ♥ @@ -424,8 +426,10 @@ func (b BBS) Bytes() []byte { } // CSS writes to dst the Cascading Style Sheets classes needed by the HTML. -// The CSS relies on cascading variables. -// See https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties for details. +// +// The CSS results rely on [custom properties] which are not supported by legacy browsers. +// +// [custom properties]: https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties. func (b BBS) CSS(dst *bytes.Buffer) error { if dst == nil { return ErrBuff @@ -440,7 +444,7 @@ func (b BBS) CSS(dst *bytes.Buffer) error { return nil } -// HTML writes to dst the HTML equivalent of BBS color codes with matching CSS color classes. +// HTML writes to dst the BBS color codes as CSS color classes within HTML elements. func (b BBS) HTML(dst *bytes.Buffer, src []byte) error { if dst == nil { return ErrBuff diff --git a/example_test.go b/example_test.go index b749f5e..38c9194 100644 --- a/example_test.go +++ b/example_test.go @@ -22,12 +22,12 @@ func Example() { } defer file.Close() - s, b, err := bbs.Fields(file) + s, name, err := bbs.Fields(file) if err != nil { log.Print(err) return } - fmt.Printf("Found %d %s color controls.\n\n", len(s), b) + fmt.Printf("Found %d %s color controls.\n\n", len(s), name) // reopen the file file, err = static.Open("static/examples/hello.pcb") diff --git a/examplebbs_test.go b/examplebbs_test.go index b963c7f..ca87d6b 100644 --- a/examplebbs_test.go +++ b/examplebbs_test.go @@ -3,35 +3,15 @@ package bbs_test import ( "bytes" "fmt" - "log" "strings" "github.com/bengarrett/bbs" ) -func ExampleHTML() { - var buf bytes.Buffer - src := strings.NewReader("@X03Hello world") - if _, err := bbs.HTML(&buf, src); err != nil { - fmt.Print(err) - } - fmt.Print(buf.String()) - // Output: Hello world -} - -func ExampleRenegadeHTML() { - var buf bytes.Buffer - src := []byte("|03Hello |07|19world") - if err := bbs.RenegadeHTML(&buf, src); err != nil { - log.Print(err) - } - fmt.Print(buf.String()) - // Output: Hello world -} - func ExampleCelerityHTML() { - var buf bytes.Buffer src := []byte("|cHello |C|S|wworld") + + var buf bytes.Buffer if err := bbs.CelerityHTML(&buf, src); err != nil { fmt.Print(err) } @@ -39,95 +19,149 @@ func ExampleCelerityHTML() { // Output: Hello world } -func ExamplePCBoardHTML() { - var buf bytes.Buffer - src := []byte("@X03Hello world") - if err := bbs.PCBoardHTML(&buf, src); err != nil { - log.Print(err) - } - fmt.Print(buf.String()) - // Output: Hello world -} - func ExampleIsCelerity() { - b := []byte("|cHello |C|S|wworld") - fmt.Printf("Is b Celerity BBS text? %v", bbs.IsCelerity(b)) - // Output: Is b Celerity BBS text? true + src := []byte("|cHello |C|S|wworld") + + fmt.Print(bbs.IsCelerity(src)) + // Output: true } func ExampleIsPCBoard() { - b := []byte("@X03Hello world") - fmt.Printf("Is b PCBoard BBS text? %v", bbs.IsPCBoard(b)) - // Output: Is b PCBoard BBS text? true + src := []byte("@X03Hello world") + + fmt.Print(bbs.IsPCBoard(src)) + // Output: true } func ExampleIsRenegade() { - b := []byte("|03Hello |07|19world") - fmt.Printf("Is b Renegade BBS text? %v", bbs.IsRenegade(b)) - // Output: Is b Renegade BBS text? true + src := []byte("|03Hello |07|19world") + + fmt.Print(bbs.IsRenegade(src)) + // Output: true } func ExampleIsTelegard() { const grave = "\u0060" // godoc treats a grave character as a special control - b := []byte(grave + "7Hello world") - fmt.Printf("Is b Telegard BBS text? %v", bbs.IsTelegard(b)) - // Output: Is b Telegard BBS text? true + src := []byte(grave + "7Hello world") + + fmt.Print(bbs.IsTelegard(src)) + // Output: true } func ExampleIsWWIVHash() { - b := []byte("|#7Hello world") - fmt.Printf("Is b WVIV BBS # text? %v", bbs.IsWWIVHash(b)) - // Output: Is b WVIV BBS # text? true + src := []byte("|#7Hello world") + + fmt.Print(bbs.IsWWIVHash(src)) + // Output: true } func ExampleIsWWIVHeart() { - b := []byte("\x037Hello world") - fmt.Printf("Is b WWIV BBS ♥ text? %v", bbs.IsWWIVHeart(b)) - // Output: Is b WWIV BBS ♥ text? true + src := []byte("\x037Hello world") + + fmt.Print(bbs.IsWWIVHeart(src)) + // Output: true } func ExampleIsWildcat() { - b := []byte("@0F@Hello world") - fmt.Printf("Is b Wildcat! BBS text? %v", bbs.IsWildcat(b)) - // Output: Is b Wildcat! BBS text? true + src := []byte("@0F@Hello world") + + fmt.Print(bbs.IsWildcat(src)) + // Output: true +} + +func ExampleHTML() { + src := strings.NewReader("@X03Hello world") + + var buf bytes.Buffer + r, err := bbs.HTML(&buf, src) + if err != nil { + fmt.Print(err) + return + } + + fmt.Printf("\n", r) + fmt.Print(buf.String()) + // Output: + // Hello world +} + +func ExampleRenegadeHTML() { + 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: Hello world +} + +func ExamplePCBoardHTML() { + src := []byte("@X03Hello world") + + var buf bytes.Buffer + if err := bbs.PCBoardHTML(&buf, src); err != nil { + fmt.Print(err) + } + fmt.Print(buf.String()) + // Output: Hello world } func ExampleTrimControls() { - b := []byte("@CLS@@PAUSE@Hello world") - r := bbs.TrimControls(b) - fmt.Print(string(r)) - // Output: Hello world + 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 ExampleFind() { - r := strings.NewReader("@X03Hello world") - f := bbs.Find(r) - fmt.Printf("Reader is in a %s BBS format", f.Name()) - // Output: Reader is in a PCBoard BBS format + src := strings.NewReader("@X03Hello world") + + f := bbs.Find(src) + fmt.Printf("Found %s text", f.Name()) + // Output: Found PCBoard text } func ExampleFind_none() { - r := strings.NewReader("Hello world") - f := bbs.Find(r) + 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 ExampleFind_ansi() { + 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("Reader is plain text") + fmt.Print("Found plain text") + return } - // Output: Reader is plain text + fmt.Printf("Found %s text", f.Name()) + // Output: Found ANSI text } func ExampleBBS_Bytes() { b := bbs.PCBoard.Bytes() - fmt.Printf("%s %v", b, b) - // Output: @X [64 88] + 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 ExampleBBS_CSS() { - var dst bytes.Buffer - if err := bbs.PCBoard.CSS(&dst); err != nil { + var css bytes.Buffer + if err := bbs.PCBoard.CSS(&css); err != nil { fmt.Print(err) } - // print the first 8 lines - lines := strings.Split(dst.String(), "\n") + // print the first 8 lines of the css + lines := strings.Split(css.String(), "\n") for i := 0; i < 8; i++ { fmt.Println(lines[i]) } @@ -142,13 +176,44 @@ func ExampleBBS_CSS() { } func ExampleBBS_HTML() { + src := []byte("@X03Hello @X04world@X00") + var buf bytes.Buffer - src := []byte("@X03Hello world") if err := bbs.PCBoard.HTML(&buf, src); err != nil { - log.Print(err) + fmt.Print(err) + return } fmt.Print(buf.String()) - // Output: Hello world + // Output: Hello world +} + +func ExampleBBS_HTML_find() { + 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: Hello world +} + +func ExampleBBS_HTML_ansi() { + 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 } func ExampleBBS_Name() { @@ -157,25 +222,27 @@ func ExampleBBS_Name() { } func ExampleBBS_Remove() { - var buf bytes.Buffer src := []byte("@X03Hello @X07world") + + var buf bytes.Buffer if err := bbs.PCBoard.Remove(&buf, src); err != nil { - log.Print(err) + fmt.Print(err) } - fmt.Printf("%q to %q", src, buf.String()) - // Output: "@X03Hello @X07world" to "Hello world" + fmt.Printf("%q to %s", src, buf.String()) + // Output: "@X03Hello @X07world" to Hello world } func ExampleBBS_Remove_find() { - var buf bytes.Buffer src := []byte("@X03Hello @X07world") - r := bytes.NewReader(src) - b := bbs.Find(r) - if err := b.Remove(&buf, src); err != nil { - log.Print(err) + + 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 %q", src, buf.String()) - // Output: "@X03Hello @X07world" to "Hello world" + fmt.Printf("%q to %s", src, buf.String()) + // Output: "@X03Hello @X07world" to Hello world } func ExampleBBS_String() { @@ -184,8 +251,29 @@ func ExampleBBS_String() { } func ExampleBBS_Valid() { - r := strings.NewReader("Hello world") - f := bbs.Find(r) - fmt.Print("Is reader BBS text? ", f.Valid()) - // Output: Is reader BBS text? false + src := "@X03Hello @X07world" + + r := strings.NewReader(src) + ok := bbs.Find(r).Valid() + fmt.Print(ok) + // Output: true +} + +func ExampleBBS_Valid_false() { + src := "Hello world" + + r := strings.NewReader(src) + ok := bbs.Find(r).Valid() + fmt.Print(ok) + // Output: false +} + +func ExampleBBS_Valid_ansi() { + 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 } diff --git a/examplefields_test.go b/examplefields_test.go index 702c9f2..ae8e05a 100644 --- a/examplefields_test.go +++ b/examplefields_test.go @@ -11,19 +11,43 @@ import ( func ExampleFields() { r := strings.NewReader("@X03Hello @XF0world") + s, b, err := bbs.Fields(r) if err != nil { log.Print(err) } - fmt.Printf("Found %d %s sequences.", len(s), b) - // Output: Found 2 PCBoard @X sequences. + + 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" + // +} + +func ExampleFields_ansi() { + 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 } func ExampleFields_none() { - r := strings.NewReader("Hello world.") - _, _, err := bbs.Fields(r) + r := strings.NewReader("Hello world") + + s, b, err := bbs.Fields(r) if errors.Is(err, bbs.ErrNone) { - fmt.Print(err) + fmt.Printf("error: %s", err) + return } - // Output: no bbs color codes found + fmt.Printf("Found %d, %s sequences\n", len(s), b) + // Output: error: no bbs color code found } diff --git a/internal/split/split.go b/internal/split/split.go index d9b1744..1426a8e 100644 --- a/internal/split/split.go +++ b/internal/split/split.go @@ -74,8 +74,8 @@ func VBarsHTML(dst *bytes.Buffer, src []byte) error { } d := colorInt{ - Foreground: -1, - Background: -1, + Foreground: 0, + Background: 0, Content: "", } bars := VBars(src)