GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: html.c,v 1.90 2017/09/06 16:24:11 schwarze Exp $ */ |
||
2 |
/* |
||
3 |
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> |
||
4 |
* Copyright (c) 2011-2015, 2017 Ingo Schwarze <schwarze@openbsd.org> |
||
5 |
* |
||
6 |
* Permission to use, copy, modify, and distribute this software for any |
||
7 |
* purpose with or without fee is hereby granted, provided that the above |
||
8 |
* copyright notice and this permission notice appear in all copies. |
||
9 |
* |
||
10 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
||
11 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||
12 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR |
||
13 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||
14 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
||
15 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
||
16 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||
17 |
*/ |
||
18 |
#include <sys/types.h> |
||
19 |
|||
20 |
#include <assert.h> |
||
21 |
#include <ctype.h> |
||
22 |
#include <stdarg.h> |
||
23 |
#include <stdio.h> |
||
24 |
#include <stdint.h> |
||
25 |
#include <stdlib.h> |
||
26 |
#include <string.h> |
||
27 |
#include <unistd.h> |
||
28 |
|||
29 |
#include "mandoc_aux.h" |
||
30 |
#include "mandoc.h" |
||
31 |
#include "roff.h" |
||
32 |
#include "out.h" |
||
33 |
#include "html.h" |
||
34 |
#include "manconf.h" |
||
35 |
#include "main.h" |
||
36 |
|||
37 |
struct htmldata { |
||
38 |
const char *name; |
||
39 |
int flags; |
||
40 |
#define HTML_NOSTACK (1 << 0) |
||
41 |
#define HTML_AUTOCLOSE (1 << 1) |
||
42 |
#define HTML_NLBEFORE (1 << 2) |
||
43 |
#define HTML_NLBEGIN (1 << 3) |
||
44 |
#define HTML_NLEND (1 << 4) |
||
45 |
#define HTML_NLAFTER (1 << 5) |
||
46 |
#define HTML_NLAROUND (HTML_NLBEFORE | HTML_NLAFTER) |
||
47 |
#define HTML_NLINSIDE (HTML_NLBEGIN | HTML_NLEND) |
||
48 |
#define HTML_NLALL (HTML_NLAROUND | HTML_NLINSIDE) |
||
49 |
#define HTML_INDENT (1 << 6) |
||
50 |
#define HTML_NOINDENT (1 << 7) |
||
51 |
}; |
||
52 |
|||
53 |
static const struct htmldata htmltags[TAG_MAX] = { |
||
54 |
{"html", HTML_NLALL}, |
||
55 |
{"head", HTML_NLALL | HTML_INDENT}, |
||
56 |
{"body", HTML_NLALL}, |
||
57 |
{"meta", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, |
||
58 |
{"title", HTML_NLAROUND}, |
||
59 |
{"div", HTML_NLAROUND}, |
||
60 |
{"h1", HTML_NLAROUND}, |
||
61 |
{"h2", HTML_NLAROUND}, |
||
62 |
{"span", 0}, |
||
63 |
{"link", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, |
||
64 |
{"br", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, |
||
65 |
{"a", 0}, |
||
66 |
{"table", HTML_NLALL | HTML_INDENT}, |
||
67 |
{"colgroup", HTML_NLALL | HTML_INDENT}, |
||
68 |
{"col", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, |
||
69 |
{"tr", HTML_NLALL | HTML_INDENT}, |
||
70 |
{"td", HTML_NLAROUND}, |
||
71 |
{"li", HTML_NLAROUND | HTML_INDENT}, |
||
72 |
{"ul", HTML_NLALL | HTML_INDENT}, |
||
73 |
{"ol", HTML_NLALL | HTML_INDENT}, |
||
74 |
{"dl", HTML_NLALL | HTML_INDENT}, |
||
75 |
{"dt", HTML_NLAROUND}, |
||
76 |
{"dd", HTML_NLAROUND | HTML_INDENT}, |
||
77 |
{"pre", HTML_NLALL | HTML_NOINDENT}, |
||
78 |
{"var", 0}, |
||
79 |
{"cite", 0}, |
||
80 |
{"b", 0}, |
||
81 |
{"i", 0}, |
||
82 |
{"code", 0}, |
||
83 |
{"small", 0}, |
||
84 |
{"style", HTML_NLALL | HTML_INDENT}, |
||
85 |
{"math", HTML_NLALL | HTML_INDENT}, |
||
86 |
{"mrow", 0}, |
||
87 |
{"mi", 0}, |
||
88 |
{"mn", 0}, |
||
89 |
{"mo", 0}, |
||
90 |
{"msup", 0}, |
||
91 |
{"msub", 0}, |
||
92 |
{"msubsup", 0}, |
||
93 |
{"mfrac", 0}, |
||
94 |
{"msqrt", 0}, |
||
95 |
{"mfenced", 0}, |
||
96 |
{"mtable", 0}, |
||
97 |
{"mtr", 0}, |
||
98 |
{"mtd", 0}, |
||
99 |
{"munderover", 0}, |
||
100 |
{"munder", 0}, |
||
101 |
{"mover", 0}, |
||
102 |
}; |
||
103 |
|||
104 |
static const char *const roffscales[SCALE_MAX] = { |
||
105 |
"cm", /* SCALE_CM */ |
||
106 |
"in", /* SCALE_IN */ |
||
107 |
"pc", /* SCALE_PC */ |
||
108 |
"pt", /* SCALE_PT */ |
||
109 |
"em", /* SCALE_EM */ |
||
110 |
"em", /* SCALE_MM */ |
||
111 |
"ex", /* SCALE_EN */ |
||
112 |
"ex", /* SCALE_BU */ |
||
113 |
"em", /* SCALE_VS */ |
||
114 |
"ex", /* SCALE_FS */ |
||
115 |
}; |
||
116 |
|||
117 |
static void a2width(const char *, struct roffsu *); |
||
118 |
static void print_byte(struct html *, char); |
||
119 |
static void print_endword(struct html *); |
||
120 |
static void print_indent(struct html *); |
||
121 |
static void print_word(struct html *, const char *); |
||
122 |
|||
123 |
static void print_ctag(struct html *, struct tag *); |
||
124 |
static int print_escape(struct html *, char); |
||
125 |
static int print_encode(struct html *, const char *, const char *, int); |
||
126 |
static void print_href(struct html *, const char *, const char *, int); |
||
127 |
static void print_metaf(struct html *, enum mandoc_esc); |
||
128 |
|||
129 |
|||
130 |
void * |
||
131 |
html_alloc(const struct manoutput *outopts) |
||
132 |
{ |
||
133 |
struct html *h; |
||
134 |
|||
135 |
144 |
h = mandoc_calloc(1, sizeof(struct html)); |
|
136 |
|||
137 |
72 |
h->tag = NULL; |
|
138 |
72 |
h->style = outopts->style; |
|
139 |
72 |
h->base_man = outopts->man; |
|
140 |
72 |
h->base_includes = outopts->includes; |
|
141 |
✗✓ | 72 |
if (outopts->fragment) |
142 |
h->oflags |= HTML_FRAGMENT; |
||
143 |
|||
144 |
72 |
return h; |
|
145 |
} |
||
146 |
|||
147 |
void |
||
148 |
html_free(void *p) |
||
149 |
{ |
||
150 |
struct tag *tag; |
||
151 |
struct html *h; |
||
152 |
|||
153 |
144 |
h = (struct html *)p; |
|
154 |
|||
155 |
✗✓ | 144 |
while ((tag = h->tag) != NULL) { |
156 |
h->tag = tag->next; |
||
157 |
free(tag); |
||
158 |
} |
||
159 |
|||
160 |
72 |
free(h); |
|
161 |
72 |
} |
|
162 |
|||
163 |
void |
||
164 |
print_gen_head(struct html *h) |
||
165 |
{ |
||
166 |
struct tag *t; |
||
167 |
|||
168 |
144 |
print_otag(h, TAG_META, "?", "charset", "utf-8"); |
|
169 |
|||
170 |
/* |
||
171 |
* Print a default style-sheet. |
||
172 |
*/ |
||
173 |
|||
174 |
72 |
t = print_otag(h, TAG_STYLE, ""); |
|
175 |
72 |
print_text(h, "table.head, table.foot { width: 100%; }"); |
|
176 |
72 |
print_endline(h); |
|
177 |
72 |
print_text(h, "td.head-rtitle, td.foot-os { text-align: right; }"); |
|
178 |
72 |
print_endline(h); |
|
179 |
72 |
print_text(h, "td.head-vol { text-align: center; }"); |
|
180 |
72 |
print_endline(h); |
|
181 |
72 |
print_text(h, "div.Pp { margin: 1ex 0ex; }"); |
|
182 |
72 |
print_tagq(h, t); |
|
183 |
|||
184 |
✗✓ | 72 |
if (h->style) |
185 |
print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet", |
||
186 |
h->style, "type", "text/css", "media", "all"); |
||
187 |
72 |
} |
|
188 |
|||
189 |
static void |
||
190 |
print_metaf(struct html *h, enum mandoc_esc deco) |
||
191 |
{ |
||
192 |
enum htmlfont font; |
||
193 |
|||
194 |
switch (deco) { |
||
195 |
case ESCAPE_FONTPREV: |
||
196 |
font = h->metal; |
||
197 |
break; |
||
198 |
case ESCAPE_FONTITALIC: |
||
199 |
font = HTMLFONT_ITALIC; |
||
200 |
break; |
||
201 |
case ESCAPE_FONTBOLD: |
||
202 |
font = HTMLFONT_BOLD; |
||
203 |
break; |
||
204 |
case ESCAPE_FONTBI: |
||
205 |
font = HTMLFONT_BI; |
||
206 |
break; |
||
207 |
case ESCAPE_FONT: |
||
208 |
case ESCAPE_FONTROMAN: |
||
209 |
font = HTMLFONT_NONE; |
||
210 |
break; |
||
211 |
default: |
||
212 |
abort(); |
||
213 |
} |
||
214 |
|||
215 |
if (h->metaf) { |
||
216 |
print_tagq(h, h->metaf); |
||
217 |
h->metaf = NULL; |
||
218 |
} |
||
219 |
|||
220 |
h->metal = h->metac; |
||
221 |
h->metac = font; |
||
222 |
|||
223 |
switch (font) { |
||
224 |
case HTMLFONT_ITALIC: |
||
225 |
h->metaf = print_otag(h, TAG_I, ""); |
||
226 |
break; |
||
227 |
case HTMLFONT_BOLD: |
||
228 |
h->metaf = print_otag(h, TAG_B, ""); |
||
229 |
break; |
||
230 |
case HTMLFONT_BI: |
||
231 |
h->metaf = print_otag(h, TAG_B, ""); |
||
232 |
print_otag(h, TAG_I, ""); |
||
233 |
break; |
||
234 |
default: |
||
235 |
break; |
||
236 |
} |
||
237 |
} |
||
238 |
|||
239 |
char * |
||
240 |
html_make_id(const struct roff_node *n) |
||
241 |
{ |
||
242 |
const struct roff_node *nch; |
||
243 |
288 |
char *buf, *cp; |
|
244 |
|||
245 |
✓✓ | 576 |
for (nch = n->child; nch != NULL; nch = nch->next) |
246 |
✗✓ | 144 |
if (nch->type != ROFFT_TEXT) |
247 |
return NULL; |
||
248 |
|||
249 |
144 |
buf = NULL; |
|
250 |
144 |
deroff(&buf, n); |
|
251 |
✗✓ | 144 |
if (buf == NULL) |
252 |
return NULL; |
||
253 |
|||
254 |
/* http://www.w3.org/TR/html5/dom.html#the-id-attribute */ |
||
255 |
|||
256 |
✓✓ | 2448 |
for (cp = buf; *cp != '\0'; cp++) |
257 |
✗✓ | 1080 |
if (*cp == ' ') |
258 |
*cp = '_'; |
||
259 |
|||
260 |
144 |
return buf; |
|
261 |
144 |
} |
|
262 |
|||
263 |
int |
||
264 |
html_strlen(const char *cp) |
||
265 |
{ |
||
266 |
size_t rsz; |
||
267 |
int skip, sz; |
||
268 |
|||
269 |
/* |
||
270 |
* Account for escaped sequences within string length |
||
271 |
* calculations. This follows the logic in term_strlen() as we |
||
272 |
* must calculate the width of produced strings. |
||
273 |
* Assume that characters are always width of "1". This is |
||
274 |
* hacky, but it gets the job done for approximation of widths. |
||
275 |
*/ |
||
276 |
|||
277 |
sz = 0; |
||
278 |
skip = 0; |
||
279 |
while (1) { |
||
280 |
rsz = strcspn(cp, "\\"); |
||
281 |
if (rsz) { |
||
282 |
cp += rsz; |
||
283 |
if (skip) { |
||
284 |
skip = 0; |
||
285 |
rsz--; |
||
286 |
} |
||
287 |
sz += rsz; |
||
288 |
} |
||
289 |
if ('\0' == *cp) |
||
290 |
break; |
||
291 |
cp++; |
||
292 |
switch (mandoc_escape(&cp, NULL, NULL)) { |
||
293 |
case ESCAPE_ERROR: |
||
294 |
return sz; |
||
295 |
case ESCAPE_UNICODE: |
||
296 |
case ESCAPE_NUMBERED: |
||
297 |
case ESCAPE_SPECIAL: |
||
298 |
case ESCAPE_OVERSTRIKE: |
||
299 |
if (skip) |
||
300 |
skip = 0; |
||
301 |
else |
||
302 |
sz++; |
||
303 |
break; |
||
304 |
case ESCAPE_SKIPCHAR: |
||
305 |
skip = 1; |
||
306 |
break; |
||
307 |
default: |
||
308 |
break; |
||
309 |
} |
||
310 |
} |
||
311 |
return sz; |
||
312 |
} |
||
313 |
|||
314 |
static int |
||
315 |
print_escape(struct html *h, char c) |
||
316 |
{ |
||
317 |
|||
318 |
✓✓✓✓ ✗✓✓✓ |
6153 |
switch (c) { |
319 |
case '<': |
||
320 |
96 |
print_word(h, "<"); |
|
321 |
96 |
break; |
|
322 |
case '>': |
||
323 |
96 |
print_word(h, ">"); |
|
324 |
96 |
break; |
|
325 |
case '&': |
||
326 |
3 |
print_word(h, "&"); |
|
327 |
3 |
break; |
|
328 |
case '"': |
||
329 |
9 |
print_word(h, """); |
|
330 |
9 |
break; |
|
331 |
case ASCII_NBRSP: |
||
332 |
print_word(h, " "); |
||
333 |
break; |
||
334 |
case ASCII_HYPH: |
||
335 |
93 |
print_byte(h, '-'); |
|
336 |
93 |
break; |
|
337 |
case ASCII_BREAK: |
||
338 |
break; |
||
339 |
default: |
||
340 |
2631 |
return 0; |
|
341 |
} |
||
342 |
297 |
return 1; |
|
343 |
2928 |
} |
|
344 |
|||
345 |
static int |
||
346 |
print_encode(struct html *h, const char *p, const char *pend, int norecurse) |
||
347 |
{ |
||
348 |
4740 |
char numbuf[16]; |
|
349 |
struct tag *t; |
||
350 |
4740 |
const char *seq; |
|
351 |
size_t sz; |
||
352 |
4740 |
int c, len, breakline, nospace; |
|
353 |
enum mandoc_esc esc; |
||
354 |
static const char rejs[10] = { ' ', '\\', '<', '>', '&', '"', |
||
355 |
ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' }; |
||
356 |
|||
357 |
✓✗ | 4740 |
if (pend == NULL) |
358 |
4740 |
pend = strchr(p, '\0'); |
|
359 |
|||
360 |
breakline = 0; |
||
361 |
nospace = 0; |
||
362 |
|||
363 |
✓✓ | 19419 |
while (p < pend) { |
364 |
✗✓✗✗ |
12159 |
if (HTML_SKIPCHAR & h->flags && '\\' != *p) { |
365 |
h->flags &= ~HTML_SKIPCHAR; |
||
366 |
p++; |
||
367 |
continue; |
||
368 |
} |
||
369 |
|||
370 |
✓✓✓✗ |
187533 |
for (sz = strcspn(p, rejs); sz-- && p < pend; p++) |
371 |
54405 |
print_byte(h, *p); |
|
372 |
|||
373 |
✗✓✗✗ |
12159 |
if (breakline && |
374 |
(p >= pend || *p == ' ' || *p == ASCII_NBRSP)) { |
||
375 |
t = print_otag(h, TAG_DIV, ""); |
||
376 |
print_text(h, "\\~"); |
||
377 |
print_tagq(h, t); |
||
378 |
breakline = 0; |
||
379 |
while (p < pend && (*p == ' ' || *p == ASCII_NBRSP)) |
||
380 |
p++; |
||
381 |
continue; |
||
382 |
} |
||
383 |
|||
384 |
✓✓ | 12159 |
if (p >= pend) |
385 |
break; |
||
386 |
|||
387 |
✓✓ | 7620 |
if (*p == ' ') { |
388 |
4974 |
print_endword(h); |
|
389 |
4974 |
p++; |
|
390 |
4974 |
continue; |
|
391 |
} |
||
392 |
|||
393 |
✓✓ | 2646 |
if (print_escape(h, *p++)) |
394 |
288 |
continue; |
|
395 |
|||
396 |
2358 |
esc = mandoc_escape(&p, &seq, &len); |
|
397 |
✓✗ | 2358 |
if (ESCAPE_ERROR == esc) |
398 |
break; |
||
399 |
|||
400 |
✗✗✗✗ ✗✗✗✓ |
2358 |
switch (esc) { |
401 |
case ESCAPE_FONT: |
||
402 |
case ESCAPE_FONTPREV: |
||
403 |
case ESCAPE_FONTBOLD: |
||
404 |
case ESCAPE_FONTITALIC: |
||
405 |
case ESCAPE_FONTBI: |
||
406 |
case ESCAPE_FONTROMAN: |
||
407 |
if (0 == norecurse) |
||
408 |
print_metaf(h, esc); |
||
409 |
continue; |
||
410 |
case ESCAPE_SKIPCHAR: |
||
411 |
h->flags |= HTML_SKIPCHAR; |
||
412 |
continue; |
||
413 |
default: |
||
414 |
break; |
||
415 |
} |
||
416 |
|||
417 |
✗✓ | 2358 |
if (h->flags & HTML_SKIPCHAR) { |
418 |
h->flags &= ~HTML_SKIPCHAR; |
||
419 |
continue; |
||
420 |
} |
||
421 |
|||
422 |
✓✓✓✗ ✗✗✗ |
2358 |
switch (esc) { |
423 |
case ESCAPE_UNICODE: |
||
424 |
/* Skip past "u" header. */ |
||
425 |
1023 |
c = mchars_num2uc(seq + 1, len - 1); |
|
426 |
1023 |
break; |
|
427 |
case ESCAPE_NUMBERED: |
||
428 |
111 |
c = mchars_num2char(seq, len); |
|
429 |
✗✓ | 111 |
if (c < 0) |
430 |
continue; |
||
431 |
break; |
||
432 |
case ESCAPE_SPECIAL: |
||
433 |
1224 |
c = mchars_spec2cp(seq, len); |
|
434 |
✓✓ | 1224 |
if (c <= 0) |
435 |
39 |
continue; |
|
436 |
break; |
||
437 |
case ESCAPE_BREAK: |
||
438 |
breakline = 1; |
||
439 |
continue; |
||
440 |
case ESCAPE_NOSPACE: |
||
441 |
if ('\0' == *p) |
||
442 |
nospace = 1; |
||
443 |
continue; |
||
444 |
case ESCAPE_OVERSTRIKE: |
||
445 |
if (len == 0) |
||
446 |
continue; |
||
447 |
c = seq[len - 1]; |
||
448 |
break; |
||
449 |
default: |
||
450 |
continue; |
||
451 |
} |
||
452 |
✓✓ | 2319 |
if ((c < 0x20 && c != 0x09) || |
453 |
✓✓ | 2265 |
(c > 0x7E && c < 0xA0)) |
454 |
96 |
c = 0xFFFD; |
|
455 |
✓✓ | 2319 |
if (c > 0x7E) { |
456 |
2037 |
(void)snprintf(numbuf, sizeof(numbuf), "&#x%.4X;", c); |
|
457 |
2037 |
print_word(h, numbuf); |
|
458 |
✓✓ | 2319 |
} else if (print_escape(h, c) == 0) |
459 |
273 |
print_byte(h, c); |
|
460 |
} |
||
461 |
|||
462 |
4740 |
return nospace; |
|
463 |
4740 |
} |
|
464 |
|||
465 |
static void |
||
466 |
print_href(struct html *h, const char *name, const char *sec, int man) |
||
467 |
{ |
||
468 |
const char *p, *pp; |
||
469 |
|||
470 |
pp = man ? h->base_man : h->base_includes; |
||
471 |
while ((p = strchr(pp, '%')) != NULL) { |
||
472 |
print_encode(h, pp, p, 1); |
||
473 |
if (man && p[1] == 'S') { |
||
474 |
if (sec == NULL) |
||
475 |
print_byte(h, '1'); |
||
476 |
else |
||
477 |
print_encode(h, sec, NULL, 1); |
||
478 |
} else if ((man && p[1] == 'N') || |
||
479 |
(man == 0 && p[1] == 'I')) |
||
480 |
print_encode(h, name, NULL, 1); |
||
481 |
else |
||
482 |
print_encode(h, p, p + 2, 1); |
||
483 |
pp = p + 2; |
||
484 |
} |
||
485 |
if (*pp != '\0') |
||
486 |
print_encode(h, pp, NULL, 1); |
||
487 |
} |
||
488 |
|||
489 |
struct tag * |
||
490 |
print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) |
||
491 |
{ |
||
492 |
5538 |
va_list ap; |
|
493 |
2769 |
struct roffsu mysu, *su; |
|
494 |
2769 |
char numbuf[16]; |
|
495 |
struct tag *t; |
||
496 |
const char *attr; |
||
497 |
char *arg1, *arg2; |
||
498 |
double v; |
||
499 |
int i, have_style, tflags; |
||
500 |
|||
501 |
2769 |
tflags = htmltags[tag].flags; |
|
502 |
|||
503 |
/* Push this tag onto the stack of open scopes. */ |
||
504 |
|||
505 |
✓✓ | 2769 |
if ((tflags & HTML_NOSTACK) == 0) { |
506 |
2697 |
t = mandoc_malloc(sizeof(struct tag)); |
|
507 |
2697 |
t->tag = tag; |
|
508 |
2697 |
t->next = h->tag; |
|
509 |
2697 |
h->tag = t; |
|
510 |
2697 |
} else |
|
511 |
t = NULL; |
||
512 |
|||
513 |
✓✓ | 2769 |
if (tflags & HTML_NLBEFORE) |
514 |
1371 |
print_endline(h); |
|
515 |
✓✓ | 2769 |
if (h->col == 0) |
516 |
1470 |
print_indent(h); |
|
517 |
✓✓ | 1299 |
else if ((h->flags & HTML_NOSPACE) == 0) { |
518 |
✗✓ | 87 |
if (h->flags & HTML_KEEP) |
519 |
print_word(h, " "); |
||
520 |
else { |
||
521 |
✗✓ | 87 |
if (h->flags & HTML_PREKEEP) |
522 |
h->flags |= HTML_KEEP; |
||
523 |
87 |
print_endword(h); |
|
524 |
} |
||
525 |
} |
||
526 |
|||
527 |
5538 |
if ( ! (h->flags & HTML_NONOSPACE)) |
|
528 |
2769 |
h->flags &= ~HTML_NOSPACE; |
|
529 |
else |
||
530 |
2769 |
h->flags |= HTML_NOSPACE; |
|
531 |
|||
532 |
/* Print out the tag name and attributes. */ |
||
533 |
|||
534 |
2769 |
print_byte(h, '<'); |
|
535 |
2769 |
print_word(h, htmltags[tag].name); |
|
536 |
|||
537 |
2769 |
va_start(ap, fmt); |
|
538 |
|||
539 |
have_style = 0; |
||
540 |
✓✓ | 8640 |
while (*fmt != '\0') { |
541 |
✓✓ | 1554 |
if (*fmt == 's') { |
542 |
have_style = 1; |
||
543 |
3 |
fmt++; |
|
544 |
3 |
break; |
|
545 |
} |
||
546 |
|||
547 |
/* Parse a non-style attribute and its arguments. */ |
||
548 |
|||
549 |
✓✗ | 4653 |
arg1 = va_arg(ap, char *); |
550 |
✓✓✓✓ ✗ |
1551 |
switch (*fmt++) { |
551 |
case 'c': |
||
552 |
attr = "class"; |
||
553 |
1020 |
break; |
|
554 |
case 'h': |
||
555 |
attr = "href"; |
||
556 |
144 |
break; |
|
557 |
case 'i': |
||
558 |
attr = "id"; |
||
559 |
144 |
break; |
|
560 |
case '?': |
||
561 |
attr = arg1; |
||
562 |
✓✓ | 729 |
arg1 = va_arg(ap, char *); |
563 |
243 |
break; |
|
564 |
default: |
||
565 |
abort(); |
||
566 |
} |
||
567 |
arg2 = NULL; |
||
568 |
✗✓ | 1551 |
if (*fmt == 'M') |
569 |
arg2 = va_arg(ap, char *); |
||
570 |
✗✓ | 1551 |
if (arg1 == NULL) |
571 |
continue; |
||
572 |
|||
573 |
/* Print the non-style attributes. */ |
||
574 |
|||
575 |
1551 |
print_byte(h, ' '); |
|
576 |
1551 |
print_word(h, attr); |
|
577 |
1551 |
print_byte(h, '='); |
|
578 |
1551 |
print_byte(h, '"'); |
|
579 |
✗✗✓✓ ✓ |
1551 |
switch (*fmt) { |
580 |
case 'I': |
||
581 |
print_href(h, arg1, NULL, 0); |
||
582 |
fmt++; |
||
583 |
break; |
||
584 |
case 'M': |
||
585 |
print_href(h, arg1, arg2, 1); |
||
586 |
fmt++; |
||
587 |
break; |
||
588 |
case 'R': |
||
589 |
144 |
print_byte(h, '#'); |
|
590 |
144 |
print_encode(h, arg1, NULL, 1); |
|
591 |
144 |
fmt++; |
|
592 |
144 |
break; |
|
593 |
case 'T': |
||
594 |
246 |
print_encode(h, arg1, NULL, 1); |
|
595 |
246 |
print_word(h, "\" title=\""); |
|
596 |
246 |
print_encode(h, arg1, NULL, 1); |
|
597 |
246 |
fmt++; |
|
598 |
246 |
break; |
|
599 |
default: |
||
600 |
1161 |
print_encode(h, arg1, NULL, 1); |
|
601 |
1161 |
break; |
|
602 |
} |
||
603 |
1551 |
print_byte(h, '"'); |
|
604 |
} |
||
605 |
|||
606 |
/* Print out styles. */ |
||
607 |
|||
608 |
✓✓ | 5544 |
while (*fmt != '\0') { |
609 |
arg1 = NULL; |
||
610 |
su = NULL; |
||
611 |
|||
612 |
/* First letter: input argument type. */ |
||
613 |
|||
614 |
✓✗✗✗ ✗ |
3 |
switch (*fmt++) { |
615 |
case 'h': |
||
616 |
✓✗ | 9 |
i = va_arg(ap, int); |
617 |
su = &mysu; |
||
618 |
3 |
SCALE_HS_INIT(su, i); |
|
619 |
3 |
break; |
|
620 |
case 's': |
||
621 |
arg1 = va_arg(ap, char *); |
||
622 |
break; |
||
623 |
case 'u': |
||
624 |
su = va_arg(ap, struct roffsu *); |
||
625 |
break; |
||
626 |
case 'w': |
||
627 |
if ((arg2 = va_arg(ap, char *)) != NULL) { |
||
628 |
su = &mysu; |
||
629 |
a2width(arg2, su); |
||
630 |
} |
||
631 |
if (*fmt == '*') { |
||
632 |
if (su != NULL && su->unit == SCALE_EN && |
||
633 |
su->scale > 5.9 && su->scale < 6.1) |
||
634 |
su = NULL; |
||
635 |
fmt++; |
||
636 |
} |
||
637 |
if (*fmt == '+') { |
||
638 |
if (su != NULL) { |
||
639 |
/* Make even bold text fit. */ |
||
640 |
su->scale *= 1.2; |
||
641 |
/* Add padding. */ |
||
642 |
su->scale += 3.0; |
||
643 |
} |
||
644 |
fmt++; |
||
645 |
} |
||
646 |
if (*fmt == '-') { |
||
647 |
if (su != NULL) |
||
648 |
su->scale *= -1.0; |
||
649 |
fmt++; |
||
650 |
} |
||
651 |
break; |
||
652 |
default: |
||
653 |
abort(); |
||
654 |
} |
||
655 |
|||
656 |
/* Second letter: style name. */ |
||
657 |
|||
658 |
✗✗✓✗ ✗✗✗ |
3 |
switch (*fmt++) { |
659 |
case 'h': |
||
660 |
attr = "height"; |
||
661 |
break; |
||
662 |
case 'i': |
||
663 |
attr = "text-indent"; |
||
664 |
break; |
||
665 |
case 'l': |
||
666 |
attr = "margin-left"; |
||
667 |
3 |
break; |
|
668 |
case 'w': |
||
669 |
attr = "width"; |
||
670 |
break; |
||
671 |
case 'W': |
||
672 |
attr = "min-width"; |
||
673 |
break; |
||
674 |
case '?': |
||
675 |
attr = arg1; |
||
676 |
arg1 = va_arg(ap, char *); |
||
677 |
break; |
||
678 |
default: |
||
679 |
abort(); |
||
680 |
} |
||
681 |
✗✓ | 3 |
if (su == NULL && arg1 == NULL) |
682 |
continue; |
||
683 |
|||
684 |
✓✗ | 3 |
if (have_style == 1) |
685 |
3 |
print_word(h, " style=\""); |
|
686 |
else |
||
687 |
print_byte(h, ' '); |
||
688 |
3 |
print_word(h, attr); |
|
689 |
3 |
print_byte(h, ':'); |
|
690 |
3 |
print_byte(h, ' '); |
|
691 |
✓✗ | 3 |
if (su != NULL) { |
692 |
3 |
v = su->scale; |
|
693 |
✗✓✗✗ |
3 |
if (su->unit == SCALE_MM && (v /= 100.0) == 0.0) |
694 |
v = 1.0; |
||
695 |
✗✓ | 3 |
else if (su->unit == SCALE_BU) |
696 |
v /= 24.0; |
||
697 |
3 |
(void)snprintf(numbuf, sizeof(numbuf), "%.2f", v); |
|
698 |
3 |
print_word(h, numbuf); |
|
699 |
3 |
print_word(h, roffscales[su->unit]); |
|
700 |
3 |
} else |
|
701 |
print_word(h, arg1); |
||
702 |
3 |
print_byte(h, ';'); |
|
703 |
have_style = 2; |
||
704 |
} |
||
705 |
✓✓ | 2769 |
if (have_style == 2) |
706 |
3 |
print_byte(h, '"'); |
|
707 |
|||
708 |
2769 |
va_end(ap); |
|
709 |
|||
710 |
/* Accommodate for "well-formed" singleton escaping. */ |
||
711 |
|||
712 |
✓✓ | 2769 |
if (HTML_AUTOCLOSE & htmltags[tag].flags) |
713 |
72 |
print_byte(h, '/'); |
|
714 |
|||
715 |
2769 |
print_byte(h, '>'); |
|
716 |
|||
717 |
✓✓ | 2769 |
if (tflags & HTML_NLBEGIN) |
718 |
720 |
print_endline(h); |
|
719 |
else |
||
720 |
2049 |
h->flags |= HTML_NOSPACE; |
|
721 |
|||
722 |
✓✓ | 2769 |
if (tflags & HTML_INDENT) |
723 |
480 |
h->indent++; |
|
724 |
✓✓ | 2769 |
if (tflags & HTML_NOINDENT) |
725 |
24 |
h->noindent++; |
|
726 |
|||
727 |
2769 |
return t; |
|
728 |
2769 |
} |
|
729 |
|||
730 |
static void |
||
731 |
print_ctag(struct html *h, struct tag *tag) |
||
732 |
{ |
||
733 |
int tflags; |
||
734 |
|||
735 |
/* |
||
736 |
* Remember to close out and nullify the current |
||
737 |
* meta-font and table, if applicable. |
||
738 |
*/ |
||
739 |
✗✓ | 5394 |
if (tag == h->metaf) |
740 |
h->metaf = NULL; |
||
741 |
✗✓ | 2697 |
if (tag == h->tblt) |
742 |
h->tblt = NULL; |
||
743 |
|||
744 |
2697 |
tflags = htmltags[tag->tag].flags; |
|
745 |
|||
746 |
✓✓ | 2697 |
if (tflags & HTML_INDENT) |
747 |
480 |
h->indent--; |
|
748 |
✓✓ | 2697 |
if (tflags & HTML_NOINDENT) |
749 |
24 |
h->noindent--; |
|
750 |
✓✓ | 2697 |
if (tflags & HTML_NLEND) |
751 |
648 |
print_endline(h); |
|
752 |
2697 |
print_indent(h); |
|
753 |
2697 |
print_byte(h, '<'); |
|
754 |
2697 |
print_byte(h, '/'); |
|
755 |
2697 |
print_word(h, htmltags[tag->tag].name); |
|
756 |
2697 |
print_byte(h, '>'); |
|
757 |
✓✓ | 2697 |
if (tflags & HTML_NLAFTER) |
758 |
1299 |
print_endline(h); |
|
759 |
|||
760 |
2697 |
h->tag = tag->next; |
|
761 |
2697 |
free(tag); |
|
762 |
2697 |
} |
|
763 |
|||
764 |
void |
||
765 |
print_gen_decls(struct html *h) |
||
766 |
{ |
||
767 |
144 |
print_word(h, "<!DOCTYPE html>"); |
|
768 |
72 |
print_endline(h); |
|
769 |
72 |
} |
|
770 |
|||
771 |
void |
||
772 |
print_text(struct html *h, const char *word) |
||
773 |
{ |
||
774 |
✓✓✓✓ |
7341 |
if (h->col && (h->flags & HTML_NOSPACE) == 0) { |
775 |
✓✗ | 72 |
if ( ! (HTML_KEEP & h->flags)) { |
776 |
✗✓ | 72 |
if (HTML_PREKEEP & h->flags) |
777 |
h->flags |= HTML_KEEP; |
||
778 |
72 |
print_endword(h); |
|
779 |
72 |
} else |
|
780 |
print_word(h, " "); |
||
781 |
} |
||
782 |
|||
783 |
✗✓ | 2943 |
assert(NULL == h->metaf); |
784 |
✗✗✗✓ |
2943 |
switch (h->metac) { |
785 |
case HTMLFONT_ITALIC: |
||
786 |
h->metaf = print_otag(h, TAG_I, ""); |
||
787 |
break; |
||
788 |
case HTMLFONT_BOLD: |
||
789 |
h->metaf = print_otag(h, TAG_B, ""); |
||
790 |
break; |
||
791 |
case HTMLFONT_BI: |
||
792 |
h->metaf = print_otag(h, TAG_B, ""); |
||
793 |
print_otag(h, TAG_I, ""); |
||
794 |
break; |
||
795 |
default: |
||
796 |
2943 |
print_indent(h); |
|
797 |
2943 |
break; |
|
798 |
} |
||
799 |
|||
800 |
✗✓ | 2943 |
assert(word); |
801 |
✓✗ | 2943 |
if ( ! print_encode(h, word, NULL, 0)) { |
802 |
✓✓ | 2943 |
if ( ! (h->flags & HTML_NONOSPACE)) |
803 |
2238 |
h->flags &= ~HTML_NOSPACE; |
|
804 |
2943 |
h->flags &= ~HTML_NONEWLINE; |
|
805 |
2943 |
} else |
|
806 |
h->flags |= HTML_NOSPACE | HTML_NONEWLINE; |
||
807 |
|||
808 |
✗✓ | 2943 |
if (h->metaf) { |
809 |
print_tagq(h, h->metaf); |
||
810 |
h->metaf = NULL; |
||
811 |
} |
||
812 |
|||
813 |
2943 |
h->flags &= ~HTML_IGNDELIM; |
|
814 |
2943 |
} |
|
815 |
|||
816 |
void |
||
817 |
print_tagq(struct html *h, const struct tag *until) |
||
818 |
{ |
||
819 |
struct tag *tag; |
||
820 |
|||
821 |
✓✓ | 5877 |
while ((tag = h->tag) != NULL) { |
822 |
2064 |
print_ctag(h, tag); |
|
823 |
✓✓✓✓ |
3984 |
if (until && tag == until) |
824 |
1533 |
return; |
|
825 |
} |
||
826 |
1677 |
} |
|
827 |
|||
828 |
void |
||
829 |
print_stagq(struct html *h, const struct tag *suntil) |
||
830 |
{ |
||
831 |
struct tag *tag; |
||
832 |
|||
833 |
✓✗ | 7422 |
while ((tag = h->tag) != NULL) { |
834 |
✓✗✓✓ |
5370 |
if (suntil && tag == suntil) |
835 |
2052 |
return; |
|
836 |
633 |
print_ctag(h, tag); |
|
837 |
} |
||
838 |
2052 |
} |
|
839 |
|||
840 |
void |
||
841 |
print_paragraph(struct html *h) |
||
842 |
{ |
||
843 |
struct tag *t; |
||
844 |
|||
845 |
t = print_otag(h, TAG_DIV, "c", "Pp"); |
||
846 |
print_tagq(h, t); |
||
847 |
} |
||
848 |
|||
849 |
|||
850 |
/*********************************************************************** |
||
851 |
* Low level output functions. |
||
852 |
* They implement line breaking using a short static buffer. |
||
853 |
***********************************************************************/ |
||
854 |
|||
855 |
/* |
||
856 |
* Buffer one HTML output byte. |
||
857 |
* If the buffer is full, flush and deactivate it and start a new line. |
||
858 |
* If the buffer is inactive, print directly. |
||
859 |
*/ |
||
860 |
static void |
||
861 |
print_byte(struct html *h, char c) |
||
862 |
{ |
||
863 |
✓✓ | 244788 |
if ((h->flags & HTML_BUFFER) == 0) { |
864 |
✓✗ | 214044 |
putchar(c); |
865 |
107022 |
h->col++; |
|
866 |
107022 |
return; |
|
867 |
} |
||
868 |
|||
869 |
✓✓ | 15372 |
if (h->col + h->bufcol < sizeof(h->buf)) { |
870 |
15264 |
h->buf[h->bufcol++] = c; |
|
871 |
15264 |
return; |
|
872 |
} |
||
873 |
|||
874 |
✓✗ | 216 |
putchar('\n'); |
875 |
108 |
h->col = 0; |
|
876 |
108 |
print_indent(h); |
|
877 |
✓✗ | 216 |
putchar(' '); |
878 |
✓✗ | 216 |
putchar(' '); |
879 |
108 |
fwrite(h->buf, h->bufcol, 1, stdout); |
|
880 |
✓✗ | 216 |
putchar(c); |
881 |
108 |
h->col = (h->indent + 1) * 2 + h->bufcol + 1; |
|
882 |
108 |
h->bufcol = 0; |
|
883 |
108 |
h->flags &= ~HTML_BUFFER; |
|
884 |
122502 |
} |
|
885 |
|||
886 |
/* |
||
887 |
* If something was printed on the current output line, end it. |
||
888 |
* Not to be called right after print_indent(). |
||
889 |
*/ |
||
890 |
void |
||
891 |
print_endline(struct html *h) |
||
892 |
{ |
||
893 |
✓✓ | 10812 |
if (h->col == 0) |
894 |
return; |
||
895 |
|||
896 |
✓✓ | 3705 |
if (h->bufcol) { |
897 |
✓✗ | 1302 |
putchar(' '); |
898 |
651 |
fwrite(h->buf, h->bufcol, 1, stdout); |
|
899 |
651 |
h->bufcol = 0; |
|
900 |
651 |
} |
|
901 |
✓✗ | 7410 |
putchar('\n'); |
902 |
3705 |
h->col = 0; |
|
903 |
3705 |
h->flags |= HTML_NOSPACE; |
|
904 |
3705 |
h->flags &= ~HTML_BUFFER; |
|
905 |
9111 |
} |
|
906 |
|||
907 |
/* |
||
908 |
* Flush the HTML output buffer. |
||
909 |
* If it is inactive, activate it. |
||
910 |
*/ |
||
911 |
static void |
||
912 |
print_endword(struct html *h) |
||
913 |
{ |
||
914 |
✓✓ | 10266 |
if (h->noindent) { |
915 |
2871 |
print_byte(h, ' '); |
|
916 |
2871 |
return; |
|
917 |
} |
||
918 |
|||
919 |
✓✓ | 2262 |
if ((h->flags & HTML_BUFFER) == 0) { |
920 |
759 |
h->col++; |
|
921 |
759 |
h->flags |= HTML_BUFFER; |
|
922 |
✓✗ | 2262 |
} else if (h->bufcol) { |
923 |
✓✗ | 3006 |
putchar(' '); |
924 |
1503 |
fwrite(h->buf, h->bufcol, 1, stdout); |
|
925 |
1503 |
h->col += h->bufcol + 1; |
|
926 |
1503 |
} |
|
927 |
2262 |
h->bufcol = 0; |
|
928 |
7395 |
} |
|
929 |
|||
930 |
/* |
||
931 |
* If at the beginning of a new output line, |
||
932 |
* perform indentation and mark the line as containing output. |
||
933 |
* Make sure to really produce some output right afterwards, |
||
934 |
* but do not use print_otag() for producing it. |
||
935 |
*/ |
||
936 |
static void |
||
937 |
print_indent(struct html *h) |
||
938 |
{ |
||
939 |
size_t i; |
||
940 |
|||
941 |
✓✓ | 14436 |
if (h->col) |
942 |
3477 |
return; |
|
943 |
|||
944 |
✓✓ | 3741 |
if (h->noindent == 0) { |
945 |
2658 |
h->col = h->indent * 2; |
|
946 |
✓✓ | 13224 |
for (i = 0; i < h->col; i++) |
947 |
✓✗ | 7908 |
putchar(' '); |
948 |
} |
||
949 |
3741 |
h->flags &= ~HTML_NOSPACE; |
|
950 |
10959 |
} |
|
951 |
|||
952 |
/* |
||
953 |
* Print or buffer some characters |
||
954 |
* depending on the current HTML output buffer state. |
||
955 |
*/ |
||
956 |
static void |
||
957 |
print_word(struct html *h, const char *cp) |
||
958 |
{ |
||
959 |
✓✓ | 118146 |
while (*cp != '\0') |
960 |
44691 |
print_byte(h, *cp++); |
|
961 |
9588 |
} |
|
962 |
|||
963 |
/* |
||
964 |
* Calculate the scaling unit passed in a `-width' argument. This uses |
||
965 |
* either a native scaling unit (e.g., 1i, 2m) or the string length of |
||
966 |
* the value. |
||
967 |
*/ |
||
968 |
static void |
||
969 |
a2width(const char *p, struct roffsu *su) |
||
970 |
{ |
||
971 |
const char *end; |
||
972 |
|||
973 |
end = a2roffsu(p, su, SCALE_MAX); |
||
974 |
if (end == NULL || *end != '\0') { |
||
975 |
su->unit = SCALE_EN; |
||
976 |
su->scale = html_strlen(p); |
||
977 |
} else if (su->scale < 0.0) |
||
978 |
su->scale = 0.0; |
||
979 |
} |
Generated by: GCOVR (Version 3.3) |