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