blob: 4d68767c3007a1e1363ab8f49629f3bba56c5dff [file] [log] [blame]
Rob Pikebf983a02009-10-13 22:10:16 -07001// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Process plain text into HTML.
6// - h2's are made from lines followed by a line "----\n"
7// - tab-indented blocks become <pre> blocks
8// - blank lines become <p> marks
9// - "quoted strings" become <code>quoted strings</code>
10
11package main
12
13import (
James Toy791a2672010-03-19 16:17:18 -070014 "bufio"
15 "bytes"
16 "log"
17 "os"
Rob Pikebf983a02009-10-13 22:10:16 -070018)
19
20var (
Rob Pike29ae8e92011-02-14 11:47:19 -080021 lines = make([][]byte, 0, 2000) // probably big enough; grows if not
Rob Pikebf983a02009-10-13 22:10:16 -070022
James Toy791a2672010-03-19 16:17:18 -070023 empty = []byte("")
24 newline = []byte("\n")
25 tab = []byte("\t")
26 quote = []byte(`"`)
Rob Pike29ae8e92011-02-14 11:47:19 -080027 indent = []byte{' ', ' ', ' ', ' '}
Rob Pikebf983a02009-10-13 22:10:16 -070028
James Toy791a2672010-03-19 16:17:18 -070029 sectionMarker = []byte("----\n")
30 preStart = []byte("<pre>")
31 preEnd = []byte("</pre>\n")
32 pp = []byte("<p>\n")
33)
Rob Pikebf983a02009-10-13 22:10:16 -070034
35func main() {
James Toy791a2672010-03-19 16:17:18 -070036 read()
37 headings()
38 paragraphs()
39 coalesce(preStart, foldPre)
40 coalesce(tab, foldTabs)
41 quotes()
42 write()
Rob Pikebf983a02009-10-13 22:10:16 -070043}
44
45func read() {
James Toy791a2672010-03-19 16:17:18 -070046 b := bufio.NewReader(os.Stdin)
Rob Pikebf983a02009-10-13 22:10:16 -070047 for {
James Toy791a2672010-03-19 16:17:18 -070048 line, err := b.ReadBytes('\n')
Rob Pikebf983a02009-10-13 22:10:16 -070049 if err == os.EOF {
James Toy791a2672010-03-19 16:17:18 -070050 break
Rob Pikebf983a02009-10-13 22:10:16 -070051 }
52 if err != nil {
Rob Pikeeea18d92011-02-01 12:47:35 -080053 log.Fatal(err)
Rob Pikebf983a02009-10-13 22:10:16 -070054 }
Rob Pike29ae8e92011-02-14 11:47:19 -080055 lines = append(lines, line)
Rob Pikebf983a02009-10-13 22:10:16 -070056 }
57}
58
59func write() {
James Toy791a2672010-03-19 16:17:18 -070060 b := bufio.NewWriter(os.Stdout)
Rob Pikebf983a02009-10-13 22:10:16 -070061 for _, line := range lines {
James Toy791a2672010-03-19 16:17:18 -070062 b.Write(expandTabs(line))
Rob Pikebf983a02009-10-13 22:10:16 -070063 }
James Toy791a2672010-03-19 16:17:18 -070064 b.Flush()
Rob Pikebf983a02009-10-13 22:10:16 -070065}
66
67// each time prefix is found on a line, call fold and replace
68// line with return value from fold.
69func coalesce(prefix []byte, fold func(i int) (n int, line []byte)) {
James Toy791a2672010-03-19 16:17:18 -070070 j := 0 // output line number goes up by one each loop
Rob Pikebf983a02009-10-13 22:10:16 -070071 for i := 0; i < len(lines); {
72 if bytes.HasPrefix(lines[i], prefix) {
James Toy791a2672010-03-19 16:17:18 -070073 nlines, block := fold(i)
74 lines[j] = block
75 i += nlines
Rob Pikebf983a02009-10-13 22:10:16 -070076 } else {
James Toy791a2672010-03-19 16:17:18 -070077 lines[j] = lines[i]
78 i++
Rob Pikebf983a02009-10-13 22:10:16 -070079 }
James Toy791a2672010-03-19 16:17:18 -070080 j++
Rob Pikebf983a02009-10-13 22:10:16 -070081 }
James Toy791a2672010-03-19 16:17:18 -070082 lines = lines[0:j]
Rob Pikebf983a02009-10-13 22:10:16 -070083}
84
85// return the <pre> block as a single slice
86func foldPre(i int) (n int, line []byte) {
James Toy791a2672010-03-19 16:17:18 -070087 buf := new(bytes.Buffer)
Rob Pikebf983a02009-10-13 22:10:16 -070088 for i < len(lines) {
James Toy791a2672010-03-19 16:17:18 -070089 buf.Write(lines[i])
90 n++
Rob Pikebf983a02009-10-13 22:10:16 -070091 if bytes.Equal(lines[i], preEnd) {
92 break
93 }
James Toy791a2672010-03-19 16:17:18 -070094 i++
Rob Pikebf983a02009-10-13 22:10:16 -070095 }
James Toy791a2672010-03-19 16:17:18 -070096 return n, buf.Bytes()
Rob Pikebf983a02009-10-13 22:10:16 -070097}
98
99// return the tab-indented block as a single <pre>-bounded slice
100func foldTabs(i int) (n int, line []byte) {
James Toy791a2672010-03-19 16:17:18 -0700101 buf := new(bytes.Buffer)
102 buf.WriteString("<pre>\n")
Rob Pikebf983a02009-10-13 22:10:16 -0700103 for i < len(lines) {
104 if !bytes.HasPrefix(lines[i], tab) {
James Toy791a2672010-03-19 16:17:18 -0700105 break
Rob Pikebf983a02009-10-13 22:10:16 -0700106 }
James Toy791a2672010-03-19 16:17:18 -0700107 buf.Write(lines[i])
108 n++
109 i++
Rob Pikebf983a02009-10-13 22:10:16 -0700110 }
James Toy791a2672010-03-19 16:17:18 -0700111 buf.WriteString("</pre>\n")
112 return n, buf.Bytes()
Rob Pikebf983a02009-10-13 22:10:16 -0700113}
114
115func headings() {
James Toy791a2672010-03-19 16:17:18 -0700116 b := bufio.NewWriter(os.Stdout)
Rob Pikebf983a02009-10-13 22:10:16 -0700117 for i, l := range lines {
118 if i > 0 && bytes.Equal(l, sectionMarker) {
James Toy791a2672010-03-19 16:17:18 -0700119 lines[i-1] = []byte("<h2>" + string(trim(lines[i-1])) + "</h2>\n")
120 lines[i] = empty
Rob Pikebf983a02009-10-13 22:10:16 -0700121 }
122 }
James Toy791a2672010-03-19 16:17:18 -0700123 b.Flush()
Rob Pikebf983a02009-10-13 22:10:16 -0700124}
125
126func paragraphs() {
127 for i, l := range lines {
128 if bytes.Equal(l, newline) {
James Toy791a2672010-03-19 16:17:18 -0700129 lines[i] = pp
Rob Pikebf983a02009-10-13 22:10:16 -0700130 }
131 }
132}
133
134func quotes() {
135 for i, l := range lines {
James Toy791a2672010-03-19 16:17:18 -0700136 lines[i] = codeQuotes(l)
Rob Pikebf983a02009-10-13 22:10:16 -0700137 }
138}
139
140func codeQuotes(l []byte) []byte {
141 if bytes.HasPrefix(l, preStart) {
142 return l
143 }
James Toy791a2672010-03-19 16:17:18 -0700144 n := bytes.Index(l, quote)
Rob Pikebf983a02009-10-13 22:10:16 -0700145 if n < 0 {
146 return l
147 }
James Toy791a2672010-03-19 16:17:18 -0700148 buf := new(bytes.Buffer)
149 inQuote := false
Rob Pikebf983a02009-10-13 22:10:16 -0700150 for _, c := range l {
151 if c == '"' {
152 if inQuote {
153 buf.WriteString("</code>")
154 } else {
155 buf.WriteString("<code>")
156 }
157 inQuote = !inQuote
158 } else {
159 buf.WriteByte(c)
160 }
161 }
James Toy791a2672010-03-19 16:17:18 -0700162 return buf.Bytes()
Rob Pikebf983a02009-10-13 22:10:16 -0700163}
164
165// drop trailing newline
166func trim(l []byte) []byte {
James Toy791a2672010-03-19 16:17:18 -0700167 n := len(l)
Rob Pikebf983a02009-10-13 22:10:16 -0700168 if n > 0 && l[n-1] == '\n' {
James Toy791a2672010-03-19 16:17:18 -0700169 return l[0 : n-1]
Rob Pikebf983a02009-10-13 22:10:16 -0700170 }
171 return l
172}
173
Rob Pike29ae8e92011-02-14 11:47:19 -0800174// expand tabs to spaces. don't worry about columns.
Rob Pikebf983a02009-10-13 22:10:16 -0700175func expandTabs(l []byte) []byte {
Rob Pike29ae8e92011-02-14 11:47:19 -0800176 return bytes.Replace(l, tab, indent, -1)
Rob Pikebf983a02009-10-13 22:10:16 -0700177}