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