File Coverage

hoedown/src/html_smartypants.c
Criterion Covered Total %
statement 0 142 0.0
branch 0 206 0.0
condition n/a
subroutine n/a
pod n/a
total 0 348 0.0


line stmt bran cond sub pod time code
1             #include "html.h"
2              
3             #include <string.h>
4             #include <stdlib.h>
5             #include <stdio.h>
6             #include <ctype.h>
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[] = {
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 inline 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 "&apos;" 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[] = { "'", "&#39;", "&#x27;", "&apos;", 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. &apos; or &#39; or &#x27;.
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 &ldquo; or &rdquo; */
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, "&rsquo;");
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, "&rsquo;");
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, "&copy;");
174 0           return 2;
175             }
176            
177 0 0         if (t1 == 'r' && t2 == ')') {
178 0           HOEDOWN_BUFPUTSL(ob, "&reg;");
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, "&trade;");
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, "&mdash;");
198 0           return 2;
199             }
200            
201 0 0         if (size >= 2 && text[1] == '-') {
    0          
202 0           HOEDOWN_BUFPUTSL(ob, "&ndash;");
203 0           return 1;
204             }
205            
206 0           hoedown_buffer_putc(ob, text[0]);
207 0           return 0;
208             }
209            
210             /* Converts &quot; 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             int len;
215 0 0         if (size >= 6 && memcmp(text, "&quot;", 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, "&#0;", 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, "&hellip;");
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, "&hellip;");
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, "&frac12;");
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, "&frac14;");
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, "&frac34;");
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, "&quot;");
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 0 0         while (i < size && text[i] != '>')
    0          
317 0           i++;
318              
319 0 0         for (tag = 0; tag < skip_tags_count; ++tag) {
320 0 0         if (hoedown_html_is_tag(text, size, skip_tags[tag]) == HOEDOWN_HTML_TAG_OPEN)
321             break;
322             }
323              
324 0 0         if (tag < skip_tags_count) {
325             for (;;) {
326 0 0         while (i < size && text[i] != '<')
    0          
327 0           i++;
328              
329 0 0         if (i == size)
330             break;
331              
332 0 0         if (hoedown_html_is_tag(text + i, size - i, skip_tags[tag]) == HOEDOWN_HTML_TAG_CLOSE)
333             break;
334              
335 0           i++;
336 0           }
337              
338 0 0         while (i < size && text[i] != '>')
    0          
339 0           i++;
340             }
341              
342 0           hoedown_buffer_put(ob, text, i + 1);
343 0           return i;
344             }
345              
346             static size_t
347 0           smartypants_cb__escape(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
348             {
349 0 0         if (size < 2)
350             return 0;
351              
352 0 0         switch (text[1]) {
    0          
353             case '\\':
354             case '"':
355             case '\'':
356             case '.':
357             case '-':
358             case '`':
359 0           hoedown_buffer_putc(ob, text[1]);
360 0           return 1;
361              
362             default:
363 0           hoedown_buffer_putc(ob, '\\');
364 0           return 0;
365             }
366             }
367              
368             #if 0
369             static struct {
370                 uint8_t c0;
371                 const uint8_t *pattern;
372                 const uint8_t *entity;
373                 int skip;
374             } smartypants_subs[] = {
375                 { '\'', "'s>", "&rsquo;", 0 },
376                 { '\'', "'t>", "&rsquo;", 0 },
377                 { '\'', "'re>", "&rsquo;", 0 },
378                 { '\'', "'ll>", "&rsquo;", 0 },
379                 { '\'', "'ve>", "&rsquo;", 0 },
380                 { '\'', "'m>", "&rsquo;", 0 },
381                 { '\'', "'d>", "&rsquo;", 0 },
382                 { '-', "--", "&mdash;", 1 },
383                 { '-', "<->", "&ndash;", 0 },
384                 { '.', "...", "&hellip;", 2 },
385                 { '.', ". . .", "&hellip;", 4 },
386                 { '(', "(c)", "&copy;", 2 },
387                 { '(', "(r)", "&reg;", 2 },
388                 { '(', "(tm)", "&trade;", 3 },
389                 { '3', "<3/4>", "&frac34;", 2 },
390                 { '3', "<3/4ths>", "&frac34;", 2 },
391                 { '1', "<1/2>", "&frac12;", 2 },
392                 { '1', "<1/4>", "&frac14;", 2 },
393                 { '1', "<1/4th>", "&frac14;", 2 },
394                 { '&', "&#0;", 0, 3 },
395             };
396             #endif
397              
398             void
399 0           hoedown_html_smartypants(hoedown_buffer *ob, const uint8_t *text, size_t size)
400             {
401             size_t i;
402 0           struct smartypants_data smrt = {0, 0};
403              
404 0 0         if (!text)
405 0           return;
406              
407 0           hoedown_buffer_grow(ob, size);
408              
409 0 0         for (i = 0; i < size; ++i) {
410             size_t org;
411             uint8_t action = 0;
412              
413             org = i;
414 0 0         while (i < size && (action = smartypants_cb_chars[text[i]]) == 0)
    0          
415 0           i++;
416              
417 0 0         if (i > org)
418 0           hoedown_buffer_put(ob, text + org, i - org);
419              
420 0 0         if (i < size) {
421 0           i += smartypants_cb_ptrs[(int)action]
422 0 0         (ob, &smrt, i ? text[i - 1] : 0, text + i, size - i);
423             }
424             }
425             }
426