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