// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Process plain text into HTML. // - h2's are made from lines followed by a line "----\n" // - tab-indented blocks become
blocks // - blank lines becomemarks // - "quoted strings" become
quoted strings
package main import ( "bufio" "bytes" "log" "os" ) var ( lines = make([][]byte, 0, 10000) // assume big enough linebuf = make([]byte, 10000) // assume big enough empty = []byte("") newline = []byte("\n") tab = []byte("\t") quote = []byte(`"`) sectionMarker = []byte("----\n") preStart = []byte("") preEnd = []byte("\n") pp = []byte("\n") ) func main() { read() headings() paragraphs() coalesce(preStart, foldPre) coalesce(tab, foldTabs) quotes() write() } func read() { b := bufio.NewReader(os.Stdin) for { line, err := b.ReadBytes('\n') if err == os.EOF { break } if err != nil { log.Exit(err) } n := len(lines) lines = lines[0 : n+1] lines[n] = line } } func write() { b := bufio.NewWriter(os.Stdout) for _, line := range lines { b.Write(expandTabs(line)) } b.Flush() } // each time prefix is found on a line, call fold and replace // line with return value from fold. func coalesce(prefix []byte, fold func(i int) (n int, line []byte)) { j := 0 // output line number goes up by one each loop for i := 0; i < len(lines); { if bytes.HasPrefix(lines[i], prefix) { nlines, block := fold(i) lines[j] = block i += nlines } else { lines[j] = lines[i] i++ } j++ } lines = lines[0:j] } // return the
block as a single slice func foldPre(i int) (n int, line []byte) { buf := new(bytes.Buffer) for i < len(lines) { buf.Write(lines[i]) n++ if bytes.Equal(lines[i], preEnd) { break } i++ } return n, buf.Bytes() } // return the tab-indented block as a single-bounded slice func foldTabs(i int) (n int, line []byte) { buf := new(bytes.Buffer) buf.WriteString("\n") for i < len(lines) { if !bytes.HasPrefix(lines[i], tab) { break } buf.Write(lines[i]) n++ i++ } buf.WriteString("\n") return n, buf.Bytes() } func headings() { b := bufio.NewWriter(os.Stdout) for i, l := range lines { if i > 0 && bytes.Equal(l, sectionMarker) { lines[i-1] = []byte("" + string(trim(lines[i-1])) + "
\n") lines[i] = empty } } b.Flush() } func paragraphs() { for i, l := range lines { if bytes.Equal(l, newline) { lines[i] = pp } } } func quotes() { for i, l := range lines { lines[i] = codeQuotes(l) } } func codeQuotes(l []byte) []byte { if bytes.HasPrefix(l, preStart) { return l } n := bytes.Index(l, quote) if n < 0 { return l } buf := new(bytes.Buffer) inQuote := false for _, c := range l { if c == '"' { if inQuote { buf.WriteString("") } else { buf.WriteString("") } inQuote = !inQuote } else { buf.WriteByte(c) } } return buf.Bytes() } // drop trailing newline func trim(l []byte) []byte { n := len(l) if n > 0 && l[n-1] == '\n' { return l[0 : n-1] } return l } // expand tabs to 4 spaces. don't worry about columns. func expandTabs(l []byte) []byte { j := 0 // position in linebuf. for _, c := range l { if c == '\t' { for k := 0; k < 4; k++ { linebuf[j] = ' ' j++ } } else { linebuf[j] = c j++ } } return linebuf[0:j] }