example/main.go (view raw)
1//
2// Blackfriday Markdown Processor
3// Available at http://github.com/russross/blackfriday
4//
5// Copyright © 2011 Russ Ross <russ@russross.com>.
6// Distributed under the Simplified BSD License.
7// See README.md for details.
8//
9
10//
11//
12// Example front-end for command-line use
13//
14//
15
16package main
17
18import (
19 "flag"
20 "fmt"
21 "io/ioutil"
22 "github.com/russross/blackfriday"
23 "os"
24 "runtime/pprof"
25 "strings"
26)
27
28const DEFAULT_TITLE = ""
29
30func main() {
31 // parse command-line options
32 var page, toc, toconly, xhtml, latex, smartypants, latexdashes, fractions bool
33 var css, cpuprofile string
34 var repeat int
35 flag.BoolVar(&page, "page", false,
36 "Generate a standalone HTML page (implies -latex=false)")
37 flag.BoolVar(&toc, "toc", false,
38 "Generate a table of contents (implies -latex=false)")
39 flag.BoolVar(&toconly, "toconly", false,
40 "Generate a table of contents only (implies -toc)")
41 flag.BoolVar(&xhtml, "xhtml", true,
42 "Use XHTML-style tags in HTML output")
43 flag.BoolVar(&latex, "latex", false,
44 "Generate LaTeX output instead of HTML")
45 flag.BoolVar(&smartypants, "smartypants", true,
46 "Apply smartypants-style substitutions")
47 flag.BoolVar(&latexdashes, "latexdashes", true,
48 "Use LaTeX-style dash rules for smartypants")
49 flag.BoolVar(&fractions, "fractions", true,
50 "Use improved fraction rules for smartypants")
51 flag.StringVar(&css, "css", "",
52 "Link to a CSS stylesheet (implies -page)")
53 flag.StringVar(&cpuprofile, "cpuprofile", "",
54 "Write cpu profile to a file")
55 flag.IntVar(&repeat, "repeat", 1,
56 "Process the input multiple times (for benchmarking)")
57 flag.Usage = func() {
58 fmt.Fprintf(os.Stderr, "Blackfriday Markdown Processor v"+blackfriday.VERSION+
59 "\nAvailable at http://github.com/russross/blackfriday\n\n"+
60 "Copyright © 2011 Russ Ross <russ@russross.com>\n"+
61 "Distributed under the Simplified BSD License\n"+
62 "See website for details\n\n"+
63 "Usage:\n"+
64 " %s [options] [inputfile [outputfile]]\n\n"+
65 "Options:\n",
66 os.Args[0])
67 flag.PrintDefaults()
68 }
69 flag.Parse()
70
71 // enforce implied options
72 if css != "" {
73 page = true
74 }
75 if page {
76 latex = false
77 }
78 if toconly {
79 toc = true
80 }
81 if toc {
82 latex = false
83 }
84
85 // turn on profiling?
86 if cpuprofile != "" {
87 f, err := os.Create(cpuprofile)
88 if err != nil {
89 fmt.Fprintln(os.Stderr, err)
90 }
91 pprof.StartCPUProfile(f)
92 defer pprof.StopCPUProfile()
93 }
94
95 // read the input
96 var input []byte
97 var err os.Error
98 args := flag.Args()
99 switch len(args) {
100 case 0:
101 if input, err = ioutil.ReadAll(os.Stdin); err != nil {
102 fmt.Fprintln(os.Stderr, "Error reading from Stdin:", err)
103 os.Exit(-1)
104 }
105 case 1, 2:
106 if input, err = ioutil.ReadFile(args[0]); err != nil {
107 fmt.Fprintln(os.Stderr, "Error reading from", args[0], ":", err)
108 os.Exit(-1)
109 }
110 default:
111 flag.Usage()
112 os.Exit(-1)
113 }
114
115 // set up options
116 extensions := 0
117 extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
118 extensions |= blackfriday.EXTENSION_TABLES
119 extensions |= blackfriday.EXTENSION_FENCED_CODE
120 extensions |= blackfriday.EXTENSION_AUTOLINK
121 extensions |= blackfriday.EXTENSION_STRIKETHROUGH
122 extensions |= blackfriday.EXTENSION_SPACE_HEADERS
123
124 var renderer blackfriday.Renderer
125 if latex {
126 // render the data into LaTeX
127 renderer = blackfriday.LatexRenderer(0)
128 } else {
129 // render the data into HTML
130 htmlFlags := 0
131 if xhtml {
132 htmlFlags |= blackfriday.HTML_USE_XHTML
133 }
134 if smartypants {
135 htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
136 }
137 if fractions {
138 htmlFlags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS
139 }
140 if latexdashes {
141 htmlFlags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
142 }
143 title := ""
144 if page {
145 htmlFlags |= blackfriday.HTML_COMPLETE_PAGE
146 title = getTitle(input)
147 }
148 if toconly {
149 htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
150 }
151 if toc {
152 htmlFlags |= blackfriday.HTML_TOC
153 }
154 renderer = blackfriday.HtmlRenderer(htmlFlags, title, css)
155 }
156
157 // parse and render
158 var output []byte
159 for i := 0; i < repeat; i++ {
160 output = blackfriday.Markdown(input, renderer, extensions)
161 }
162
163 // output the result
164 var out *os.File
165 if len(args) == 2 {
166 if out, err = os.Create(args[1]); err != nil {
167 fmt.Fprintf(os.Stderr, "Error creating %s: %v", args[1], err)
168 os.Exit(-1)
169 }
170 defer out.Close()
171 } else {
172 out = os.Stdout
173 }
174
175 if _, err = out.Write(output); err != nil {
176 fmt.Fprintln(os.Stderr, "Error writing output:", err)
177 os.Exit(-1)
178 }
179}
180
181// try to guess the title from the input buffer
182// just check if it starts with an <h1> element and use that
183func getTitle(input []byte) string {
184 i := 0
185
186 // skip blank lines
187 for i < len(input) && (input[i] == '\n' || input[i] == '\r') {
188 i++
189 }
190 if i >= len(input) {
191 return DEFAULT_TITLE
192 }
193 if input[i] == '\r' && i+1 < len(input) && input[i+1] == '\n' {
194 i++
195 }
196
197 // find the first line
198 start := i
199 for i < len(input) && input[i] != '\n' && input[i] != '\r' {
200 i++
201 }
202 line1 := input[start:i]
203 if input[i] == '\r' && i+1 < len(input) && input[i+1] == '\n' {
204 i++
205 }
206 i++
207
208 // check for a prefix header
209 if len(line1) >= 3 && line1[0] == '#' && (line1[1] == ' ' || line1[1] == '\t') {
210 return strings.TrimSpace(string(line1[2:]))
211 }
212
213 // check for an underlined header
214 if i >= len(input) || input[i] != '=' {
215 return DEFAULT_TITLE
216 }
217 for i < len(input) && input[i] == '=' {
218 i++
219 }
220 for i < len(input) && (input[i] == ' ' || input[i] == '\t') {
221 i++
222 }
223 if i >= len(input) || (input[i] != '\n' && input[i] != '\r') {
224 return DEFAULT_TITLE
225 }
226
227 return strings.TrimSpace(string(line1))
228}