1 /* $Id: html.c,v 1.207 2017/02/05 20:22:04 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 "config.h" 19 20 #include <sys/types.h> 21 22 #include <assert.h> 23 #include <ctype.h> 24 #include <stdarg.h> 25 #include <stdio.h> 26 #include <stdint.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "mandoc.h" 32 #include "mandoc_aux.h" 33 #include "out.h" 34 #include "html.h" 35 #include "manconf.h" 36 #include "main.h" 37 38 struct htmldata { 39 const char *name; 40 int flags; 41 #define HTML_NOSTACK (1 << 0) 42 #define HTML_AUTOCLOSE (1 << 1) 43 #define HTML_NLBEFORE (1 << 2) 44 #define HTML_NLBEGIN (1 << 3) 45 #define HTML_NLEND (1 << 4) 46 #define HTML_NLAFTER (1 << 5) 47 #define HTML_NLAROUND (HTML_NLBEFORE | HTML_NLAFTER) 48 #define HTML_NLINSIDE (HTML_NLBEGIN | HTML_NLEND) 49 #define HTML_NLALL (HTML_NLAROUND | HTML_NLINSIDE) 50 #define HTML_INDENT (1 << 6) 51 #define HTML_NOINDENT (1 << 7) 52 }; 53 54 static const struct htmldata htmltags[TAG_MAX] = { 55 {"html", HTML_NLALL}, 56 {"head", HTML_NLALL | HTML_INDENT}, 57 {"body", HTML_NLALL}, 58 {"meta", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, 59 {"title", HTML_NLAROUND}, 60 {"div", HTML_NLAROUND}, 61 {"h1", HTML_NLAROUND}, 62 {"h2", HTML_NLAROUND}, 63 {"span", 0}, 64 {"link", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, 65 {"br", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, 66 {"a", 0}, 67 {"table", HTML_NLALL | HTML_INDENT}, 68 {"colgroup", HTML_NLALL | HTML_INDENT}, 69 {"col", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, 70 {"tr", HTML_NLALL | HTML_INDENT}, 71 {"td", HTML_NLAROUND}, 72 {"li", HTML_NLAROUND | HTML_INDENT}, 73 {"ul", HTML_NLALL | HTML_INDENT}, 74 {"ol", HTML_NLALL | HTML_INDENT}, 75 {"dl", HTML_NLALL | HTML_INDENT}, 76 {"dt", HTML_NLAROUND}, 77 {"dd", HTML_NLAROUND | HTML_INDENT}, 78 {"pre", HTML_NLALL | HTML_NOINDENT}, 79 {"var", 0}, 80 {"cite", 0}, 81 {"b", 0}, 82 {"i", 0}, 83 {"code", 0}, 84 {"small", 0}, 85 {"style", HTML_NLALL | HTML_INDENT}, 86 {"math", HTML_NLALL | HTML_INDENT}, 87 {"mrow", 0}, 88 {"mi", 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 h = mandoc_calloc(1, sizeof(struct html)); 136 137 h->tag = NULL; 138 h->style = outopts->style; 139 h->base_man = outopts->man; 140 h->base_includes = outopts->includes; 141 if (outopts->fragment) 142 h->oflags |= HTML_FRAGMENT; 143 144 return h; 145 } 146 147 void 148 html_free(void *p) 149 { 150 struct tag *tag; 151 struct html *h; 152 153 h = (struct html *)p; 154 155 while ((tag = h->tag) != NULL) { 156 h->tag = tag->next; 157 free(tag); 158 } 159 160 free(h); 161 } 162 163 void 164 print_gen_head(struct html *h) 165 { 166 struct tag *t; 167 168 print_otag(h, TAG_META, "?", "charset", "utf-8"); 169 170 /* 171 * Print a default style-sheet. 172 */ 173 174 t = print_otag(h, TAG_STYLE, ""); 175 print_text(h, "table.head, table.foot { width: 100%; }"); 176 print_endline(h); 177 print_text(h, "td.head-rtitle, td.foot-os { text-align: right; }"); 178 print_endline(h); 179 print_text(h, "td.head-vol { text-align: center; }"); 180 print_endline(h); 181 print_text(h, "div.Pp { margin: 1ex 0ex; }"); 182 print_tagq(h, t); 183 184 if (h->style) 185 print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet", 186 h->style, "type", "text/css", "media", "all"); 187 } 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 int 240 html_strlen(const char *cp) 241 { 242 size_t rsz; 243 int skip, sz; 244 245 /* 246 * Account for escaped sequences within string length 247 * calculations. This follows the logic in term_strlen() as we 248 * must calculate the width of produced strings. 249 * Assume that characters are always width of "1". This is 250 * hacky, but it gets the job done for approximation of widths. 251 */ 252 253 sz = 0; 254 skip = 0; 255 while (1) { 256 rsz = strcspn(cp, "\\"); 257 if (rsz) { 258 cp += rsz; 259 if (skip) { 260 skip = 0; 261 rsz--; 262 } 263 sz += rsz; 264 } 265 if ('\0' == *cp) 266 break; 267 cp++; 268 switch (mandoc_escape(&cp, NULL, NULL)) { 269 case ESCAPE_ERROR: 270 return sz; 271 case ESCAPE_UNICODE: 272 case ESCAPE_NUMBERED: 273 case ESCAPE_SPECIAL: 274 case ESCAPE_OVERSTRIKE: 275 if (skip) 276 skip = 0; 277 else 278 sz++; 279 break; 280 case ESCAPE_SKIPCHAR: 281 skip = 1; 282 break; 283 default: 284 break; 285 } 286 } 287 return sz; 288 } 289 290 static int 291 print_escape(struct html *h, char c) 292 { 293 294 switch (c) { 295 case '<': 296 print_word(h, "<"); 297 break; 298 case '>': 299 print_word(h, ">"); 300 break; 301 case '&': 302 print_word(h, "&"); 303 break; 304 case '"': 305 print_word(h, """); 306 break; 307 case ASCII_NBRSP: 308 print_word(h, " "); 309 break; 310 case ASCII_HYPH: 311 print_byte(h, '-'); 312 break; 313 case ASCII_BREAK: 314 break; 315 default: 316 return 0; 317 } 318 return 1; 319 } 320 321 static int 322 print_encode(struct html *h, const char *p, const char *pend, int norecurse) 323 { 324 char numbuf[16]; 325 size_t sz; 326 int c, len, nospace; 327 const char *seq; 328 enum mandoc_esc esc; 329 static const char rejs[9] = { '\\', '<', '>', '&', '"', 330 ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' }; 331 332 if (pend == NULL) 333 pend = strchr(p, '\0'); 334 335 nospace = 0; 336 337 while (p < pend) { 338 if (HTML_SKIPCHAR & h->flags && '\\' != *p) { 339 h->flags &= ~HTML_SKIPCHAR; 340 p++; 341 continue; 342 } 343 344 for (sz = strcspn(p, rejs); sz-- && p < pend; p++) 345 if (*p == ' ') 346 print_endword(h); 347 else 348 print_byte(h, *p); 349 350 if (p >= pend) 351 break; 352 353 if (print_escape(h, *p++)) 354 continue; 355 356 esc = mandoc_escape(&p, &seq, &len); 357 if (ESCAPE_ERROR == esc) 358 break; 359 360 switch (esc) { 361 case ESCAPE_FONT: 362 case ESCAPE_FONTPREV: 363 case ESCAPE_FONTBOLD: 364 case ESCAPE_FONTITALIC: 365 case ESCAPE_FONTBI: 366 case ESCAPE_FONTROMAN: 367 if (0 == norecurse) 368 print_metaf(h, esc); 369 continue; 370 case ESCAPE_SKIPCHAR: 371 h->flags |= HTML_SKIPCHAR; 372 continue; 373 default: 374 break; 375 } 376 377 if (h->flags & HTML_SKIPCHAR) { 378 h->flags &= ~HTML_SKIPCHAR; 379 continue; 380 } 381 382 switch (esc) { 383 case ESCAPE_UNICODE: 384 /* Skip past "u" header. */ 385 c = mchars_num2uc(seq + 1, len - 1); 386 break; 387 case ESCAPE_NUMBERED: 388 c = mchars_num2char(seq, len); 389 if (c < 0) 390 continue; 391 break; 392 case ESCAPE_SPECIAL: 393 c = mchars_spec2cp(seq, len); 394 if (c <= 0) 395 continue; 396 break; 397 case ESCAPE_NOSPACE: 398 if ('\0' == *p) 399 nospace = 1; 400 continue; 401 case ESCAPE_OVERSTRIKE: 402 if (len == 0) 403 continue; 404 c = seq[len - 1]; 405 break; 406 default: 407 continue; 408 } 409 if ((c < 0x20 && c != 0x09) || 410 (c > 0x7E && c < 0xA0)) 411 c = 0xFFFD; 412 if (c > 0x7E) { 413 (void)snprintf(numbuf, sizeof(numbuf), "&#%d;", c); 414 print_word(h, numbuf); 415 } else if (print_escape(h, c) == 0) 416 print_byte(h, c); 417 } 418 419 return nospace; 420 } 421 422 static void 423 print_href(struct html *h, const char *name, const char *sec, int man) 424 { 425 const char *p, *pp; 426 427 pp = man ? h->base_man : h->base_includes; 428 while ((p = strchr(pp, '%')) != NULL) { 429 print_encode(h, pp, p, 1); 430 if (man && p[1] == 'S') { 431 if (sec == NULL) 432 print_byte(h, '1'); 433 else 434 print_encode(h, sec, NULL, 1); 435 } else if ((man && p[1] == 'N') || 436 (man == 0 && p[1] == 'I')) 437 print_encode(h, name, NULL, 1); 438 else 439 print_encode(h, p, p + 2, 1); 440 pp = p + 2; 441 } 442 if (*pp != '\0') 443 print_encode(h, pp, NULL, 1); 444 } 445 446 struct tag * 447 print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) 448 { 449 va_list ap; 450 struct roffsu mysu, *su; 451 char numbuf[16]; 452 struct tag *t; 453 const char *attr; 454 char *arg1, *arg2; 455 double v; 456 int i, have_style, tflags; 457 458 tflags = htmltags[tag].flags; 459 460 /* Push this tag onto the stack of open scopes. */ 461 462 if ((tflags & HTML_NOSTACK) == 0) { 463 t = mandoc_malloc(sizeof(struct tag)); 464 t->tag = tag; 465 t->next = h->tag; 466 h->tag = t; 467 } else 468 t = NULL; 469 470 if (tflags & HTML_NLBEFORE) 471 print_endline(h); 472 if (h->col == 0) 473 print_indent(h); 474 else if ((h->flags & HTML_NOSPACE) == 0) { 475 if (h->flags & HTML_KEEP) 476 print_word(h, " "); 477 else { 478 if (h->flags & HTML_PREKEEP) 479 h->flags |= HTML_KEEP; 480 print_endword(h); 481 } 482 } 483 484 if ( ! (h->flags & HTML_NONOSPACE)) 485 h->flags &= ~HTML_NOSPACE; 486 else 487 h->flags |= HTML_NOSPACE; 488 489 /* Print out the tag name and attributes. */ 490 491 print_byte(h, '<'); 492 print_word(h, htmltags[tag].name); 493 494 va_start(ap, fmt); 495 496 have_style = 0; 497 while (*fmt != '\0') { 498 if (*fmt == 's') { 499 have_style = 1; 500 fmt++; 501 break; 502 } 503 504 /* Parse a non-style attribute and its arguments. */ 505 506 arg1 = va_arg(ap, char *); 507 switch (*fmt++) { 508 case 'c': 509 attr = "class"; 510 break; 511 case 'h': 512 attr = "href"; 513 break; 514 case 'i': 515 attr = "id"; 516 break; 517 case '?': 518 attr = arg1; 519 arg1 = va_arg(ap, char *); 520 break; 521 default: 522 abort(); 523 } 524 arg2 = NULL; 525 if (*fmt == 'M') 526 arg2 = va_arg(ap, char *); 527 if (arg1 == NULL) 528 continue; 529 530 /* Print the non-style attributes. */ 531 532 print_byte(h, ' '); 533 print_word(h, attr); 534 print_byte(h, '='); 535 print_byte(h, '"'); 536 switch (*fmt) { 537 case 'M': 538 print_href(h, arg1, arg2, 1); 539 fmt++; 540 break; 541 case 'I': 542 print_href(h, arg1, NULL, 0); 543 fmt++; 544 break; 545 case 'R': 546 print_byte(h, '#'); 547 fmt++; 548 /* FALLTHROUGH */ 549 default: 550 print_encode(h, arg1, NULL, 1); 551 break; 552 } 553 print_byte(h, '"'); 554 } 555 556 /* Print out styles. */ 557 558 while (*fmt != '\0') { 559 arg1 = NULL; 560 su = NULL; 561 562 /* First letter: input argument type. */ 563 564 switch (*fmt++) { 565 case 'h': 566 i = va_arg(ap, int); 567 su = &mysu; 568 SCALE_HS_INIT(su, i); 569 break; 570 case 's': 571 arg1 = va_arg(ap, char *); 572 break; 573 case 'u': 574 su = va_arg(ap, struct roffsu *); 575 break; 576 case 'v': 577 i = va_arg(ap, int); 578 su = &mysu; 579 SCALE_VS_INIT(su, i); 580 break; 581 case 'w': 582 case 'W': 583 if ((arg2 = va_arg(ap, char *)) == NULL) 584 break; 585 su = &mysu; 586 a2width(arg2, su); 587 if (fmt[-1] == 'W') 588 su->scale *= -1.0; 589 break; 590 default: 591 abort(); 592 } 593 594 /* Second letter: style name. */ 595 596 switch (*fmt++) { 597 case 'b': 598 attr = "margin-bottom"; 599 break; 600 case 'h': 601 attr = "height"; 602 break; 603 case 'i': 604 attr = "text-indent"; 605 break; 606 case 'l': 607 attr = "margin-left"; 608 break; 609 case 't': 610 attr = "margin-top"; 611 break; 612 case 'w': 613 attr = "width"; 614 break; 615 case 'W': 616 attr = "min-width"; 617 break; 618 case '?': 619 attr = arg1; 620 arg1 = va_arg(ap, char *); 621 break; 622 default: 623 abort(); 624 } 625 if (su == NULL && arg1 == NULL) 626 continue; 627 628 if (have_style == 1) 629 print_word(h, " style=\""); 630 else 631 print_byte(h, ' '); 632 print_word(h, attr); 633 print_byte(h, ':'); 634 print_byte(h, ' '); 635 if (su != NULL) { 636 v = su->scale; 637 if (su->unit == SCALE_MM && (v /= 100.0) == 0.0) 638 v = 1.0; 639 else if (su->unit == SCALE_BU) 640 v /= 24.0; 641 (void)snprintf(numbuf, sizeof(numbuf), "%.2f", v); 642 print_word(h, numbuf); 643 print_word(h, roffscales[su->unit]); 644 } else 645 print_word(h, arg1); 646 print_byte(h, ';'); 647 have_style = 2; 648 } 649 if (have_style == 2) 650 print_byte(h, '"'); 651 652 va_end(ap); 653 654 /* Accommodate for "well-formed" singleton escaping. */ 655 656 if (HTML_AUTOCLOSE & htmltags[tag].flags) 657 print_byte(h, '/'); 658 659 print_byte(h, '>'); 660 661 if (tflags & HTML_NLBEGIN) 662 print_endline(h); 663 else 664 h->flags |= HTML_NOSPACE; 665 666 if (tflags & HTML_INDENT) 667 h->indent++; 668 if (tflags & HTML_NOINDENT) 669 h->noindent++; 670 671 return t; 672 } 673 674 static void 675 print_ctag(struct html *h, struct tag *tag) 676 { 677 int tflags; 678 679 /* 680 * Remember to close out and nullify the current 681 * meta-font and table, if applicable. 682 */ 683 if (tag == h->metaf) 684 h->metaf = NULL; 685 if (tag == h->tblt) 686 h->tblt = NULL; 687 688 tflags = htmltags[tag->tag].flags; 689 690 if (tflags & HTML_INDENT) 691 h->indent--; 692 if (tflags & HTML_NOINDENT) 693 h->noindent--; 694 if (tflags & HTML_NLEND) 695 print_endline(h); 696 print_indent(h); 697 print_byte(h, '<'); 698 print_byte(h, '/'); 699 print_word(h, htmltags[tag->tag].name); 700 print_byte(h, '>'); 701 if (tflags & HTML_NLAFTER) 702 print_endline(h); 703 704 h->tag = tag->next; 705 free(tag); 706 } 707 708 void 709 print_gen_decls(struct html *h) 710 { 711 print_word(h, "<!DOCTYPE html>"); 712 print_endline(h); 713 } 714 715 void 716 print_text(struct html *h, const char *word) 717 { 718 if (h->col && (h->flags & HTML_NOSPACE) == 0) { 719 if ( ! (HTML_KEEP & h->flags)) { 720 if (HTML_PREKEEP & h->flags) 721 h->flags |= HTML_KEEP; 722 print_endword(h); 723 } else 724 print_word(h, " "); 725 } 726 727 assert(NULL == h->metaf); 728 switch (h->metac) { 729 case HTMLFONT_ITALIC: 730 h->metaf = print_otag(h, TAG_I, ""); 731 break; 732 case HTMLFONT_BOLD: 733 h->metaf = print_otag(h, TAG_B, ""); 734 break; 735 case HTMLFONT_BI: 736 h->metaf = print_otag(h, TAG_B, ""); 737 print_otag(h, TAG_I, ""); 738 break; 739 default: 740 print_indent(h); 741 break; 742 } 743 744 assert(word); 745 if ( ! print_encode(h, word, NULL, 0)) { 746 if ( ! (h->flags & HTML_NONOSPACE)) 747 h->flags &= ~HTML_NOSPACE; 748 h->flags &= ~HTML_NONEWLINE; 749 } else 750 h->flags |= HTML_NOSPACE | HTML_NONEWLINE; 751 752 if (h->metaf) { 753 print_tagq(h, h->metaf); 754 h->metaf = NULL; 755 } 756 757 h->flags &= ~HTML_IGNDELIM; 758 } 759 760 void 761 print_tagq(struct html *h, const struct tag *until) 762 { 763 struct tag *tag; 764 765 while ((tag = h->tag) != NULL) { 766 print_ctag(h, tag); 767 if (until && tag == until) 768 return; 769 } 770 } 771 772 void 773 print_stagq(struct html *h, const struct tag *suntil) 774 { 775 struct tag *tag; 776 777 while ((tag = h->tag) != NULL) { 778 if (suntil && tag == suntil) 779 return; 780 print_ctag(h, tag); 781 } 782 } 783 784 void 785 print_paragraph(struct html *h) 786 { 787 struct tag *t; 788 789 t = print_otag(h, TAG_DIV, "c", "Pp"); 790 print_tagq(h, t); 791 } 792 793 794 /*********************************************************************** 795 * Low level output functions. 796 * They implement line breaking using a short static buffer. 797 ***********************************************************************/ 798 799 /* 800 * Buffer one HTML output byte. 801 * If the buffer is full, flush and deactivate it and start a new line. 802 * If the buffer is inactive, print directly. 803 */ 804 static void 805 print_byte(struct html *h, char c) 806 { 807 if ((h->flags & HTML_BUFFER) == 0) { 808 putchar(c); 809 h->col++; 810 return; 811 } 812 813 if (h->col + h->bufcol < sizeof(h->buf)) { 814 h->buf[h->bufcol++] = c; 815 return; 816 } 817 818 putchar('\n'); 819 h->col = 0; 820 print_indent(h); 821 putchar(' '); 822 putchar(' '); 823 fwrite(h->buf, h->bufcol, 1, stdout); 824 putchar(c); 825 h->col = (h->indent + 1) * 2 + h->bufcol + 1; 826 h->bufcol = 0; 827 h->flags &= ~HTML_BUFFER; 828 } 829 830 /* 831 * If something was printed on the current output line, end it. 832 * Not to be called right after print_indent(). 833 */ 834 void 835 print_endline(struct html *h) 836 { 837 if (h->col == 0) 838 return; 839 840 if (h->bufcol) { 841 putchar(' '); 842 fwrite(h->buf, h->bufcol, 1, stdout); 843 h->bufcol = 0; 844 } 845 putchar('\n'); 846 h->col = 0; 847 h->flags |= HTML_NOSPACE; 848 h->flags &= ~HTML_BUFFER; 849 } 850 851 /* 852 * Flush the HTML output buffer. 853 * If it is inactive, activate it. 854 */ 855 static void 856 print_endword(struct html *h) 857 { 858 if (h->noindent) { 859 print_byte(h, ' '); 860 return; 861 } 862 863 if ((h->flags & HTML_BUFFER) == 0) { 864 h->col++; 865 h->flags |= HTML_BUFFER; 866 } else if (h->bufcol) { 867 putchar(' '); 868 fwrite(h->buf, h->bufcol, 1, stdout); 869 h->col += h->bufcol + 1; 870 } 871 h->bufcol = 0; 872 } 873 874 /* 875 * If at the beginning of a new output line, 876 * perform indentation and mark the line as containing output. 877 * Make sure to really produce some output right afterwards, 878 * but do not use print_otag() for producing it. 879 */ 880 static void 881 print_indent(struct html *h) 882 { 883 size_t i; 884 885 if (h->col) 886 return; 887 888 if (h->noindent == 0) { 889 h->col = h->indent * 2; 890 for (i = 0; i < h->col; i++) 891 putchar(' '); 892 } 893 h->flags &= ~HTML_NOSPACE; 894 } 895 896 /* 897 * Print or buffer some characters 898 * depending on the current HTML output buffer state. 899 */ 900 static void 901 print_word(struct html *h, const char *cp) 902 { 903 while (*cp != '\0') 904 print_byte(h, *cp++); 905 } 906 907 /* 908 * Calculate the scaling unit passed in a `-width' argument. This uses 909 * either a native scaling unit (e.g., 1i, 2m) or the string length of 910 * the value. 911 */ 912 static void 913 a2width(const char *p, struct roffsu *su) 914 { 915 if (a2roffsu(p, su, SCALE_MAX) < 2) { 916 su->unit = SCALE_EN; 917 su->scale = html_strlen(p); 918 } else if (su->scale < 0.0) 919 su->scale = 0.0; 920 } 921