File Coverage

hoedown/src/html_smartypants.c
Criterion Covered Total %
statement 0 147 0.0
branch 0 214 0.0
condition n/a
subroutine n/a
pod n/a
total 0 361 0.0


line stmt bran cond sub pod time code
1             #include "html.h"
2              
3             #include
4             #include
5             #include
6             #include
7              
8             #ifdef _MSC_VER
9             #define snprintf _snprintf
10             #endif
11              
12             struct smartypants_data {
13             int in_squote;
14             int in_dquote;
15             };
16              
17             static size_t smartypants_cb__ltag(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
18             static size_t smartypants_cb__dquote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
19             static size_t smartypants_cb__amp(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
20             static size_t smartypants_cb__period(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
21             static size_t smartypants_cb__number(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
22             static size_t smartypants_cb__dash(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
23             static size_t smartypants_cb__parens(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
24             static size_t smartypants_cb__squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
25             static size_t smartypants_cb__backtick(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
26             static size_t smartypants_cb__escape(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
27              
28             static size_t (*smartypants_cb_ptrs[])
29             (hoedown_buffer *, struct smartypants_data *, uint8_t, const uint8_t *, size_t) =
30             {
31             NULL, /* 0 */
32             smartypants_cb__dash, /* 1 */
33             smartypants_cb__parens, /* 2 */
34             smartypants_cb__squote, /* 3 */
35             smartypants_cb__dquote, /* 4 */
36             smartypants_cb__amp, /* 5 */
37             smartypants_cb__period, /* 6 */
38             smartypants_cb__number, /* 7 */
39             smartypants_cb__ltag, /* 8 */
40             smartypants_cb__backtick, /* 9 */
41             smartypants_cb__escape, /* 10 */
42             };
43              
44             static const uint8_t smartypants_cb_chars[UINT8_MAX+1] = {
45             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
46             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
47             0, 0, 4, 0, 0, 0, 5, 3, 2, 0, 0, 0, 0, 1, 6, 0,
48             0, 7, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0,
49             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
50             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0,
51             9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
54             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
57             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
58             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61             };
62              
63             static int
64 0           word_boundary(uint8_t c)
65             {
66 0 0         return c == 0 || isspace(c) || ispunct(c);
    0          
67             }
68              
69             /*
70             If 'text' begins with any kind of single quote (e.g. "'" or "'" etc.),
71             returns the length of the sequence of characters that makes up the single-
72             quote. Otherwise, returns zero.
73             */
74             static size_t
75 0           squote_len(const uint8_t *text, size_t size)
76             {
77             static char* single_quote_list[] = { "'", "'", "'", "'", NULL };
78             char** p;
79              
80 0 0         for (p = single_quote_list; *p; ++p) {
81 0           size_t len = strlen(*p);
82 0 0         if (size >= len && memcmp(text, *p, len) == 0) {
    0          
83             return len;
84             }
85             }
86              
87             return 0;
88             }
89              
90             /* Converts " or ' at very beginning or end of a word to left or right quote */
91             static int
92 0           smartypants_quotes(hoedown_buffer *ob, uint8_t previous_char, uint8_t next_char, uint8_t quote, int *is_open)
93             {
94             char ent[8];
95              
96 0 0         if (*is_open && !word_boundary(next_char))
    0          
97             return 0;
98              
99 0 0         if (!(*is_open) && !word_boundary(previous_char))
    0          
100             return 0;
101              
102 0 0         snprintf(ent, sizeof(ent), "&%c%cquo;", (*is_open) ? 'r' : 'l', quote);
103 0           *is_open = !(*is_open);
104 0           hoedown_buffer_puts(ob, ent);
105 0           return 1;
106             }
107              
108             /*
109             Converts ' to left or right single quote; but the initial ' might be in
110             different forms, e.g. ' or ' or '.
111             'squote_text' points to the original single quote, and 'squote_size' is its length.
112             'text' points at the last character of the single-quote, e.g. ' or ;
113             */
114             static size_t
115 0           smartypants_squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size,
116             const uint8_t *squote_text, size_t squote_size)
117             {
118 0 0         if (size >= 2) {
119 0           uint8_t t1 = tolower(text[1]);
120 0           size_t next_squote_len = squote_len(text+1, size-1);
121              
122             /* convert '' to “ or ” */
123 0 0         if (next_squote_len > 0) {
124 0 0         uint8_t next_char = (size > 1+next_squote_len) ? text[1+next_squote_len] : 0;
125 0 0         if (smartypants_quotes(ob, previous_char, next_char, 'd', &smrt->in_dquote))
126             return next_squote_len;
127             }
128              
129             /* Tom's, isn't, I'm, I'd */
130 0 0         if ((t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') &&
    0          
    0          
131 0 0         (size == 3 || word_boundary(text[2]))) {
132 0           HOEDOWN_BUFPUTSL(ob, "’");
133 0           return 0;
134             }
135              
136             /* you're, you'll, you've */
137 0 0         if (size >= 3) {
138 0           uint8_t t2 = tolower(text[2]);
139              
140 0 0         if (((t1 == 'r' && t2 == 'e') ||
    0          
141 0 0         (t1 == 'l' && t2 == 'l') ||
142 0 0         (t1 == 'v' && t2 == 'e')) &&
143 0 0         (size == 4 || word_boundary(text[3]))) {
144 0           HOEDOWN_BUFPUTSL(ob, "’");
145 0           return 0;
146             }
147             }
148             }
149              
150 0 0         if (smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 's', &smrt->in_squote))
    0          
151             return 0;
152              
153 0           hoedown_buffer_put(ob, squote_text, squote_size);
154 0           return 0;
155             }
156              
157             /* Converts ' to left or right single quote. */
158             static size_t
159 0           smartypants_cb__squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
160             {
161 0           return smartypants_squote(ob, smrt, previous_char, text, size, text, 1);
162             }
163              
164             /* Converts (c), (r), (tm) */
165             static size_t
166 0           smartypants_cb__parens(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
167             {
168 0 0         if (size >= 3) {
169 0           uint8_t t1 = tolower(text[1]);
170 0           uint8_t t2 = tolower(text[2]);
171              
172 0 0         if (t1 == 'c' && t2 == ')') {
173 0           HOEDOWN_BUFPUTSL(ob, "©");
174 0           return 2;
175             }
176              
177 0 0         if (t1 == 'r' && t2 == ')') {
178 0           HOEDOWN_BUFPUTSL(ob, "®");
179 0           return 2;
180             }
181              
182 0 0         if (size >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')') {
    0          
    0          
183 0           HOEDOWN_BUFPUTSL(ob, "™");
184 0           return 3;
185             }
186             }
187              
188 0           hoedown_buffer_putc(ob, text[0]);
189 0           return 0;
190             }
191              
192             /* Converts "--" to em-dash, etc. */
193             static size_t
194 0           smartypants_cb__dash(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
195             {
196 0 0         if (size >= 3 && text[1] == '-' && text[2] == '-') {
    0          
    0          
197 0           HOEDOWN_BUFPUTSL(ob, "—");
198 0           return 2;
199             }
200              
201 0 0         if (size >= 2 && text[1] == '-') {
    0          
202 0           HOEDOWN_BUFPUTSL(ob, "–");
203 0           return 1;
204             }
205              
206 0           hoedown_buffer_putc(ob, text[0]);
207 0           return 0;
208             }
209              
210             /* Converts " etc. */
211             static size_t
212 0           smartypants_cb__amp(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
213             {
214             size_t len;
215 0 0         if (size >= 6 && memcmp(text, """, 6) == 0) {
    0          
216 0 0         if (smartypants_quotes(ob, previous_char, size >= 7 ? text[6] : 0, 'd', &smrt->in_dquote))
    0          
217             return 5;
218             }
219              
220 0           len = squote_len(text, size);
221 0 0         if (len > 0) {
222 0           return (len-1) + smartypants_squote(ob, smrt, previous_char, text+(len-1), size-(len-1), text, len);
223             }
224              
225 0 0         if (size >= 4 && memcmp(text, "�", 4) == 0)
    0          
226             return 3;
227              
228 0           hoedown_buffer_putc(ob, '&');
229 0           return 0;
230             }
231              
232             /* Converts "..." to ellipsis */
233             static size_t
234 0           smartypants_cb__period(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
235             {
236 0 0         if (size >= 3 && text[1] == '.' && text[2] == '.') {
    0          
    0          
237 0           HOEDOWN_BUFPUTSL(ob, "…");
238 0           return 2;
239             }
240              
241 0 0         if (size >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.') {
    0          
    0          
    0          
    0          
242 0           HOEDOWN_BUFPUTSL(ob, "…");
243 0           return 4;
244             }
245              
246 0           hoedown_buffer_putc(ob, text[0]);
247 0           return 0;
248             }
249              
250             /* Converts `` to opening double quote */
251             static size_t
252 0           smartypants_cb__backtick(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
253             {
254 0 0         if (size >= 2 && text[1] == '`') {
    0          
255 0 0         if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote))
    0          
256             return 1;
257             }
258              
259 0           hoedown_buffer_putc(ob, text[0]);
260 0           return 0;
261             }
262              
263             /* Converts 1/2, 1/4, 3/4 */
264             static size_t
265 0           smartypants_cb__number(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
266             {
267 0 0         if (word_boundary(previous_char) && size >= 3) {
    0          
268 0 0         if (text[0] == '1' && text[1] == '/' && text[2] == '2') {
    0          
    0          
269 0 0         if (size == 3 || word_boundary(text[3])) {
    0          
270 0           HOEDOWN_BUFPUTSL(ob, "½");
271 0           return 2;
272             }
273             }
274              
275 0 0         if (text[0] == '1' && text[1] == '/' && text[2] == '4') {
    0          
    0          
276 0 0         if (size == 3 || word_boundary(text[3]) ||
    0          
    0          
277 0 0         (size >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h')) {
    0          
278 0           HOEDOWN_BUFPUTSL(ob, "¼");
279 0           return 2;
280             }
281             }
282              
283 0 0         if (text[0] == '3' && text[1] == '/' && text[2] == '4') {
    0          
    0          
284 0 0         if (size == 3 || word_boundary(text[3]) ||
    0          
    0          
285 0 0         (size >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's')) {
    0          
    0          
286 0           HOEDOWN_BUFPUTSL(ob, "¾");
287 0           return 2;
288             }
289             }
290             }
291              
292 0           hoedown_buffer_putc(ob, text[0]);
293 0           return 0;
294             }
295              
296             /* Converts " to left or right double quote */
297             static size_t
298 0           smartypants_cb__dquote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
299             {
300 0 0         if (!smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 'd', &smrt->in_dquote))
    0          
301 0           HOEDOWN_BUFPUTSL(ob, """);
302              
303 0           return 0;
304             }
305              
306             static size_t
307 0           smartypants_cb__ltag(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
308             {
309             static const char *skip_tags[] = {
310             "pre", "code", "var", "samp", "kbd", "math", "script", "style"
311             };
312             static const size_t skip_tags_count = 8;
313              
314             size_t tag, i = 0;
315              
316             /* This is a comment. Copy everything verbatim until --> or EOF is seen. */
317 0 0         if (i + 4 < size && memcmp(text + i, "", 3) != 0)
    0          
320 0           i++;
321             i += 3;
322 0           hoedown_buffer_put(ob, text, i + 1);
323 0           return i;
324             }
325              
326 0 0         while (i < size && text[i] != '>')
    0          
327 0           i++;
328              
329 0 0         for (tag = 0; tag < skip_tags_count; ++tag) {
330 0 0         if (hoedown_html_is_tag(text, size, skip_tags[tag]) == HOEDOWN_HTML_TAG_OPEN)
331             break;
332             }
333              
334 0 0         if (tag < skip_tags_count) {
335             for (;;) {
336 0 0         while (i < size && text[i] != '<')
    0          
337 0           i++;
338              
339 0 0         if (i == size)
340             break;
341              
342 0 0         if (hoedown_html_is_tag(text + i, size - i, skip_tags[tag]) == HOEDOWN_HTML_TAG_CLOSE)
343             break;
344              
345 0           i++;
346 0           }
347              
348 0 0         while (i < size && text[i] != '>')
    0          
349 0           i++;
350             }
351              
352 0           hoedown_buffer_put(ob, text, i + 1);
353 0           return i;
354             }
355              
356             static size_t
357 0           smartypants_cb__escape(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
358             {
359 0 0         if (size < 2)
360             return 0;
361              
362 0 0         switch (text[1]) {
    0          
363             case '\\':
364             case '"':
365             case '\'':
366             case '.':
367             case '-':
368             case '`':
369 0           hoedown_buffer_putc(ob, text[1]);
370 0           return 1;
371              
372             default:
373 0           hoedown_buffer_putc(ob, '\\');
374 0           return 0;
375             }
376             }
377              
378             #if 0
379             static struct {
380             uint8_t c0;
381             const uint8_t *pattern;
382             const uint8_t *entity;
383             int skip;
384             } smartypants_subs[] = {
385             { '\'', "'s>", "’", 0 },
386             { '\'', "'t>", "’", 0 },
387             { '\'', "'re>", "’", 0 },
388             { '\'', "'ll>", "’", 0 },
389             { '\'', "'ve>", "’", 0 },
390             { '\'', "'m>", "’", 0 },
391             { '\'', "'d>", "’", 0 },
392             { '-', "--", "—", 1 },
393             { '-', "<->", "–", 0 },
394             { '.', "...", "…", 2 },
395             { '.', ". . .", "…", 4 },
396             { '(', "(c)", "©", 2 },
397             { '(', "(r)", "®", 2 },
398             { '(', "(tm)", "™", 3 },
399             { '3', "<3/4>", "¾", 2 },
400             { '3', "<3/4ths>", "¾", 2 },
401             { '1', "<1/2>", "½", 2 },
402             { '1', "<1/4>", "¼", 2 },
403             { '1', "<1/4th>", "¼", 2 },
404             { '&', "�", 0, 3 },
405             };
406             #endif
407              
408             void
409 0           hoedown_html_smartypants(hoedown_buffer *ob, const uint8_t *text, size_t size)
410             {
411             size_t i;
412 0           struct smartypants_data smrt = {0, 0};
413              
414 0 0         if (!text)
415 0           return;
416              
417 0           hoedown_buffer_grow(ob, size);
418              
419 0 0         for (i = 0; i < size; ++i) {
420             size_t org;
421             uint8_t action = 0;
422              
423             org = i;
424 0 0         while (i < size && (action = smartypants_cb_chars[text[i]]) == 0)
    0          
425 0           i++;
426              
427 0 0         if (i > org)
428 0           hoedown_buffer_put(ob, text + org, i - org);
429              
430 0 0         if (i < size) {
431 0 0         i += smartypants_cb_ptrs[(int)action]
432 0           (ob, &smrt, i ? text[i - 1] : 0, text + i, size - i);
433             }
434             }
435             }